bin/SnsMsSqlPsModule.xml

<?xml version="1.0"?>
<doc>
    <assembly>
        <name>SnsMsSqlPsModule</name>
    </assembly>
    <members>
        <member name="T:SnsMsSqlPsModule.InvokeSnsMsSqlObjectInsert">
            <summary>
            <para type="synopsis">
                This Cmdlet Imports Collection Of Objects Into Specified Table Within Specified DataBase Into Specified MS SQL
                Server And Instance.
            </para>
            <para type="description"> </para>
            <para type="description"> </para>
            <para type="description"> </para>
            <para type="description">
                This Cmdlet Imports Collection Of Objects Into Specified Table Within Specified DataBase Into Specified MS SQL
                Server And Instance.
            </para>
            <para type="description">
                This CmdLet Does Allow Users Without Or With Limited Knowledge About SQL Query Language To Work With DataBases.
                The CmdLet Creates The SQL Query And The SQL Parameters On Its Own. The User Has No Need Even To Know What SQL
                Injection Is And How To Avoid It.
            </para>
            <para type="description">
                There Are Cases When We Have Large Collections Of Similar Objects That Corresponds Exactly To Specific Table In A
                DataBase And We Need To Insert Them At Once Taking The Advantage Of The SQL Transactions. This CmdLet Is Made
                Exactly For Those Cases. Like For Example We Need To Insert The Information About All Users In The Environment
                Into A DataBase Table Named Users. Then We Simply Query The Active Directory About All Users And Pipeline The
                Output To This CmdLet.
            </para>
            <para type="description"> </para>
            <para type="description"> </para>
            <para type="description"> </para>
            <para type="description">
                The CmdLet Have Three Parameter Sets Which Represent The Allowed Combination Of Input Parameters Based On The Way
                That The CmdLet Connects To The Destination DataBase.
            </para>
            <para type="description">
                The CmdLet Support Only Authenticated Connection The MS SQL Server Instance. There Are Three Possible Ways This To
                Be Achieved:
            </para>
            <para type="description">
                - Using The Windows Authentication - The CmdLet Uses The Windows Single Sign On To Connect To The DataBase Server
                Instance As The Currently Logged On User. It Is Preferable For Scripts Executed In Interactive Mode Which
                Eliminates The Need OF Additional Logon Steps. Whenever The Script Or The Automation Are Executed Unattended On
                Schedule As A Service This Might Lead To Unexpected Behavior. In Those Cases Is Preferable To Be Used Some Of The
                Next Authentication Options. Using This Option Can Be Authenticated Only Domain Or Windows Local Accounts.
            </para>
            <para type="description"> </para>
            <para type="description">
                - Via Specifying UserName And Password In Clear Text - In This Way, The UserName And The Password Are Included In
                The SQL Connection String, That Way Send Over The Network. In This Way, The User Relies Only On The Encryption Of
                The SQL Connection. Using This Method Impersonation Of The User That Connects To The DataBase Is Possible. Which
                Does Allow The Automations To Be Run With One Service Account And The DataBase Connection To Be Established With
                Different Service Account. Using This Option Can Be Authenticated Either Domain Or Windows Local Accounts Or SQL
                Users.
            </para>
            <para type="description">
                - Via Specifying [System.Management.Automation.PSCredential] Object. In This Way, The Password Is Not Kept In
                Clear Text. This Is Considered As More Secure. The Authentication Happens In The Same Way As The Windows
                Authenticates The Users. Therefore The Password Cannot Be Intercepted During The Authentication Even If The SQL
                Connection Encryption Is Already Breached. Using This Method Impersonation Of The User That Connects To The
                DataBase Is Possible. Which Does Allow The Automations To Be Run With One Service Account And The DataBase
                Connection To Be Established With Different Service Account. Using This Option Can Be Authenticated Only SQL Users.
            </para>
            <para type="description"> </para>
            <para type="description"> </para>
            <para type="description"> </para>
            <para type="description">
                In The Begin Method The CmdLet Initializes A Connection To The Destination MS SQL DataBase And Queries It About
                The System Time. Thus Way Verify The Connection.
            </para>
            <para type="description">
                Then CmdLet Queries The Destination Database About The Schema Of The Destination Table. Using The Schema And The
                Value Specified In "ConflictClause" Parameter, The CmdLet Creates Automatically The SQL Query Which Will Be Used
                For Inserting Of The Specified Objects. The SQL Query And The SQL Parameters Which Will Be Used In The SQL Query
                Are Enumerated Solley On The Table Schema And The Input Objects Are Not Evaluated At All. This Would Means That If
                The Input Objects Have Property With Name That Corresponds To A Column With The Same Name In The Destination
                Table, Then The Value Of That Property Will Be Inserted In The Corresponding Cell On The Corresponding Row Of The
                Table. If The Object Have No Property That Corresponds To Certain Column Will Be Inserted DB NULL Value. If The
                Object Contains Properties Which Have No Corresponding Columns The Values Of Those Properties Will Be Ignored. In
                Human Language Using The Example Above About The Users Table, Would Mean That We Can Pipeline All The AD Accounts
                To The CmdLet And Only The AD Attributes With Columns In The Destination Table Schema Will Be Inserted The Rest AD
                Attributes Will Be Ignored. The Columns That Do Not Correspond To Any AD Attribute Will Have Value DB NULL.
            </para>
            <para type="description">
                Whenever The Destination Table Have An Autoincrement Identity Column, The Value Taken From The Input Objects Will
                Be Not Included In The INSERT And / Or UPDATE SQL Queries.
            </para>
            <para type="description">
                The CmdLet Enumerates The Table Primary Key From The Table Schema. Whenever The Primary Key Column Is Enumerated
                Properly The CmdLet Uses Different SQL Queries Depending On The Value Specified In ConflictClause CmdLet
                Parameter. This Does Allow Any Possible Conflicts And Primary Key Uniqueness Violations To Be Managed By The
                CmdLet And Insert The Specified Objects Without Errors. Please Keep In Mind That The CmdLet Is Intended To Be Used
                For Bulk Uploads As Fast As Possible. Therefore It Uses A Single Transaction To Insert All Of The Objects.
                Therefore In Case An Error Is Thrown All The Inserted Within The Transaction Data Will Be Reverted. It Is Not
                Possible To Preserve The Data Inserted Before The Error. If That Is Needed Please Consider Either Splitting The
                Data On Batches, Or Use Invoke-SnsMsSqlQuery In A Way So Every Insert To Be Different Transaction. The
                ConflictClause Parameter Accepts Only Predefined Values. For More Details Please Check The Parameter Section
                Bellow. It Is Important To Avoid Using The Options For Error Handling Whenever The Destination Table Have
                Autoincrement Column. This Increases The Complexity And You Might Receive Unexpected Results. Avoiding The Usage
                Of This Parameter When The Destination Table Have Autoincrement Columns Is Only A Recommendation, Programmatically
                The Usage Of That Parameter Is Not Restricted So The Users That Can Manage The Additional Complexity To Be Able To
                Take Advantage Of That. In Case The Destination Table Have No Primary Key Or The CmdLet Fail To Enumerate It, The
                Values Specified Within The ConflictClause Parameter Is Ignored And The CmdLet Process The Data With "Fail"
                Option. If That Happens The User Is Informed With A Warning.
            </para>
            <para type="description">
                Using The Enumerated Table Schema And The ConflictClause, The CmdLet Generates The SQL Query That Will Be Used For
                Insert / Update The Data And The SQL Parameters. In Order To Eliminate The Possibility About SQL Injection, The
                CmdLet Manages The Data Using SQL Parameters. Having The Table Schema Already Enumerated, The CmdLet Uses
                Parameters Add Method To Initialize The Parameters With Explicitly Specified Type And Length. This Improves The
                CmdLet Performance, Since There Is No Need OBS To Evaluate The Specified Data.
            </para>
            <para type="description"> </para>
            <para type="description"> </para>
            <para type="description"> </para>
            <para type="description">
                During The Processing Of The Objects Coming From The Pipeline, Each Object Is Converted To SQL Parameters And
                Inserted Using The Already Enumerated SQL Query. For The Conversion Are Used The Column Names From The Destination
                Table Schema. In Case The Input Object Have No Property That Corresponds To The Table Column The Enumerated
                Parameter Have Value DBNULL. The Input Object Properties That Have No Corresponding Columns In The Destination
                Table Are Ignored. Column Name Matching Against The Input Object Property Name Is Case Sensitive Exact Matching.
                Whenever The Value Of A Property Have Type That Does Not Match The Destination Column DataType, The SQL Server
                Will Try To Cast The Value. If Cast Is Not Possible SQL Error Is Thrown And The Whole Transaction Is Reverted.
            </para>
            <para type="description">
                During The Insert The Values In Properties From The Input Objects That Correspond To Columns In The Destination
                Table Will Be Inserted In The Corresponding Columns. The Values In Input Object Properties That Do Not Correspond
                To A Column In The Destination Table Will Be Ignored. The Columns In The Destination Table With No Matching
                Properties In The Input Objects Will Have Value NULL.
            </para>
            <para type="description">
                When All Input Objects Are Processed And There Are No Errors The Transaction Is Committed. During The Processing,
                The Destination Table Is Locked. At The End The CmdLet Reverts In The Output PowerShell Stream An Integer That
                Indicates The Number Of Affected Rows.</para>
            <para type="description"> </para>
            <para type="description"> </para>
            <para type="description"> </para>
            <list type="alertSet">
                <item>
                    <term> </term>
                    <description>
                        <para>AUTHOR: Svetoslav Nedyalkov Savov</para>
                        <para>THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK</para>
                        <para>OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.</para>
                        <para></para>
                        <para></para>
                        <para></para>
                    </description>
                </item>
            </list>
            <example>
              <code>
                [System.Int32]$intRows = Invoke-SnsMsSqlObjectInsert -Computer "DB01.contoso.com\Test" -DatabaseName "TestDB" `
                -Table "AdAccounts" -InputObject $arrAccounts -ConflictClause "Update" -UserName "John.Smith" -Password "Pa$$w0rd";
              </code>
              <para> </para>
              <para>
                Converts The Specified InputObjects Collection To SQL Parameters And Insert The Data Into "AdAccounts" Table. In
                Case The Table Already Contains Data The Existing Entries Will Be Updated.
              </para>
              <para> </para>
              <para>
                The CmdLet Connects To The Specified DataBase And Instance Using UserName And Password Specified In Clear Text. Of
                Course The Example Values Above Are Fake.
              </para>
              <para> </para>
              <para> </para>
              <para> </para>
            </example>
            <example>
              <code>
                $objCred = Get-Credential;
                [System.Int32]$intRows = $arrAccounts | Invoke-SnsMsSqlObjectInsert -Computer "DB01.contoso.com\Test" `
                -DatabaseName "TestDB" -Table "AdAccounts" -ConflictClause "Ignore" -Credentials $objCred;
              </code>
              <para> </para>
              <para>
                 Converts The Pipelined InputObjects Collection To SQL Parameters And Insert The Data Into AdAccounts Table. In
                 Case There Are Entries That Conflicts With Existing Table Entries The CmdLet Will Ignore The Specific Objects
                 Coming From The Pipeline And Will Complete The Transaction If There Are No Other SQL Errors.
              </para>
              <para> </para>
              <para>
                 The CmdLet Connects To The Specified DataBase And Instance Using The Credential Object Generated On The First Line.
              </para>
              <para> </para>
              <para> </para>
              <para> </para>
            </example>
            <example>
              <code>
                [System.Int32]$intRows = $arrAccounts | Invoke-SnsMsSqlObjectInsert -Computer "DB01.contoso.com\Test" `
                -DatabaseName "TestDB" -Table "AdAccounts" -ConflictClause "Ignore" -UseCurrentLogOnSession;
              </code>
              <para> </para>
              <para>
                In This Example The CmdLet Connects To The Specified DataBase And Instance Using The Currently Logged On User
                Security Context. From DataBase Perspective On This Example Is Done Exactly The Same As On The Previous Example.
              </para>
              <para> </para>
              <para> </para>
              <para> </para>
            </example>
            </summary>
            <para type="link" uri="https://github.com/svesavov/SnsMsSqlPsModule"> svesavov / SnsMsSqlPsModule - </para>
            <para type="link" uri="https://www.powershellgallery.com/packages/SnsMsSqlPsModule/"> PowerShell Gallery - </para>
            <para type="link" uri="https://www.linkedin.com/in/svetoslavsavov"> Svetoslav Savov on LinkedIn - </para>
            <para type="link" uri="https://www.microsoft.com/en-us/sql-server/sql-server-downloads"> MS SQL Server Download - </para>
            <para type="link" uri="https://docs.microsoft.com/en-us/sql/t-sql/data-types/data-types-transact-sql"> MS SQL Data Types - </para>
            <para type="link" uri="https://docs.microsoft.com/en-us/sql/t-sql/language-reference"> MS SQL Supported SQL Syntax - </para>
            <para type="link" uri="https://www.sqlservertutorial.net/"> MS SQL Tutorials - </para>
        </member>
        <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlObjectInsert.Computer">
            <summary>
            <para type="description">Specifies A MS SQL Server And MS SQL Instance In Format:</para>
            <para type="description">"DataBase Server"</para>
            <para type="description">"DataBase Server"\"DataBase Instance"</para>
            <para type="description">"DataBase Server","Port Number"</para>
            </summary>
        </member>
        <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlObjectInsert.DatabaseName">
            <summary>
            <para type="description">Specifies A DataBase Name</para>
            </summary>
        </member>
        <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlObjectInsert.UserName">
            <summary>
            <para type="description">Specifies A UserName In Clear Text</para>
            </summary>
        </member>
        <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlObjectInsert.Password">
            <summary>
            <para type="description">Specifies A [System.Security.SecureString] Or [System.String] Password To Use In The MS SQL Connection.</para>
            </summary>
        </member>
        <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlObjectInsert.Credentials">
            <summary>
            <para type="description">Specifies A [System.Management.Automation.PSCredential] Object</para>
            </summary>
        </member>
        <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlObjectInsert.UseCurrentLogOnSession">
            <summary>
            <para type="description">Specifies That Current User LogOnSession Will Be Used For The SQL Connection</para>
            </summary>
        </member>
        <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlObjectInsert.ConnectionString">
            <summary>
            <para type="description">Specifies An SQL Connection String</para>
            </summary>
        </member>
        <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlObjectInsert.Table">
            <summary>
            <para type="description">Specifies A Table Where The Data Will Be Inserted.</para>
            <para type="description">The Table Must Exist Inside The Specified DataBase, The CmdLet Won't Create It.</para>
            </summary>
        </member>
        <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlObjectInsert.InputObject">
            <summary>
            <para type="description">Specifies A Collection Of Input Objects To Be Inserted Into The DataBase Table.</para>
            <para type="description">Input Object Property Values Must Be Struct Or String.</para>
            <para type="description">Input Object Property Values Must Not Be Collections Or Other Objects.</para>
            <para type="description">Input Object Property Names Must Meet The Syntax Requirements For SQL Variables.</para>
            <para type="description">Input Object Property Names Must Match The Destination Table Column Names.</para>
            </summary>
        </member>
        <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlObjectInsert.ConflictClause">
            <summary>
            <para type="description">Specifies A Conflict Clause To Use In Case A Conflict Occurs During Insert.</para>
            <para type="description"></para>
            <para type="description">Accepted Values: "Fail", "Ignore" And "Update". Default Value "Fail".</para>
            <para type="description"></para>
            <para type="description">When The Destination Table Have Autoincrement Column, Use The Default Value To Avoid Unexpected Results.</para>
            <para type="description"></para>
            <para type="description">
                Using This Parameter Allows The CmdLet To Finish The Insert Without Errors Related With Primary Key Uniqueness
                Violation. This Is Possible With Additional Verifications.
            </para>
            <para type="description"></para>
            <para type="description">
                Whenever The Destination Table Have No Primary Key Column The CmdLet Will Ignore The Value Specified In The
                Parameter And Will Use "Fail" Option. The User Will Be Notified With Warning.
            </para>
            <para type="description">
                The Uniqueness Of The Inserted Entries Is Not Verified On The Properties / Columns Which Are Not Primary Key.
                Whenever The Destination Table Have Unique Constraints On Not Primary Key Columns And That Uniqueness Is
                Violated, The CmdLet Reverts SQL Error And The Destination Table Is Reverted Back To Its Original State.
            </para>
            <para type="description"></para>
            <para type="description"></para>
            <para type="description"></para>
            <para type="description">
                "Fail" - This Is The Default Value. The CmdLet Does Not Make Any Additional Verifications. In Case Primary Key
                Uniqueness Violation Occur The CmdLet Will Revert SQL Error. The Destination Table Is Reverted To Its State
                Prior The CmdLet Execution.
            </para>
            <para type="description">
                "Ignore" - The CmdLet Verifies Whether An Entry With The Value In The Primary Key Column Does Not Exists. Then
                It Inserts The New Entry. In Case The Entry Already Exists It Remain Intact And, New Entry Is Inserted.
                Whenever That Option Is Used The Generated SQL Query Will Look Like, Assuming "Col1" Is Primary Key:
            </para>
            <para type="description">IF NOT EXISTS</para>
            <para type="description">(SELECT [Col1] FROM [&lt;Table&gt;] WHERE [Col1] = @Var1)</para>
            <para type="description">BEGIN</para>
            <para type="description">INSERT INTO [&lt;Table&gt;]</para>
            <para type="description">([Col1], [Col2], ... [ColX])</para>
            <para type="description">VALUES (@Var1, @Var2, ... @VarX)</para>
            <para type="description">END</para>
            <para type="description"></para>
            <para type="description">
                "Update" - The CmdLet Verifies Whether An Entry With The Value In The Primary Key Column Exists. Then Either
                Inserts A New Entry Or Updates The Existing Entry. Whenever That Option Is Used The Generated SQL Query Will
                Look Like, Assuming "Col1" Is Primary Key:
            </para>
            <para type="description"></para>
            <para type="description">MERGE INTO [&lt;Table&gt;] AS [target]</para>
            <para type="description">USING (SELECT @Var1 AS [Col1], @Var2 AS [Col2] ... @VarX AS [ColX]) AS [source]</para>
            <para type="description">ON [source].[Col1] = [target].[Col1]</para>
            <para type="description">WHEN MATCHED THEN</para>
            <para type="description">UPDATE SET [Col2] = [source].[Col2] ... [ColX] = [source].[ColX]</para>
            <para type="description">WHEN NOT MATCHED THEN</para>
            <para type="description">INSERT ([Col1], [Col2], ... [ColX])</para>
            <para type="description">VALUES ([source].[Col1], [source].[Col2], ... [source].[ColX]);</para>
            <para type="description"></para>
            <para type="description"></para>
            <para type="description"></para>
            </summary>
        </member>
        <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlObjectInsert.QueryTimeout">
            <summary>
            <para type="description">Sets The Wait Time In Seconds Before Terminating The Attempt To Execute A Command And Generating An Error</para>
            <para type="description">Use 0 For The Default TimeOut Value In .NET And MS SQL Server</para>
            </summary>
        </member>
        <member name="T:SnsMsSqlPsModule.InvokeSnsMsSqlQuery">
            <summary>
            <para type="synopsis">
                This Cmdlet Connects To A Specified MS Transactional SQL DataBase, Executes SQL Query / Queries Against The
                DataBase, Reverts The Output In A Collection Of Specified DataType And Closes The SQL Session.
            </para>
            <para type="description"> </para>
            <para type="description"> </para>
            <para type="description"> </para>
            <para type="description">
                This Cmdlet Connects To A Specified MS Transactional SQL DataBase, Executes SQL Query / Queries Against The
                DataBase, Reverts The Output In A Collection Of Specified DataType And Closes The SQL Session.
            </para>
            <para type="description"> </para>
            <para type="description"> </para>
            <para type="description"> </para>
            <para type="description">
                The CmdLet Is Based On My Previous Work Related With SQLite Serverless DataBase:
            </para>
            <para type="description">
                https://github.com/svesavov/SnsSqlitePsModule
            </para>
            <para type="description">
                https://www.powershellgallery.com/packages/SnsSqlitePsModule
            </para>
            <para type="description">
                Working With SQLite Is Nice And Free, It Is Good Tool For Learning SQL. Also It Is Fine To Be Used Within
                Automations And Scripts To Keep Temporary Data Or Configuration Data (If The Machine That Hosts The Automation
                Have Fast Hard Disks), However Where It Comes To Large Volumes Of Data Accessed By Different Automations Or
                Scripts Arises The Need Of Some More Powerful Server / Service Based DataBase Which Is Optimized For Large Volumes
                Of Data, And Can Handle The DataBase Locks Better.
            </para>
            <para type="description"> </para>
            <para type="description"> </para>
            <para type="description"> </para>
            <para type="description">
                The CmdLet Have Six Parameter Sets Which Represent The Allowed Combination Of Input Parameters.
            </para>
            <para type="description">
                Like The Original CmdLet The Input Is Validated At The Beginning Within The Parameter Definitions. For That
                Purpose, Additional Validation Attributes Were Developed, Because "[ValidateScript({})]" Validation Attribute Is
                Not Supported In C#.
            </para>
            <para type="description">
                The CmdLet Connects To The Specified MS SQL Server Instance And Specified DataBase By Itself. There Is No
                Requirement SqlConnection To Be Established In Advance. In The Begin Method The CmdLet Evaluate The Provided
                Input, Imports Any .SQL Files If Such Are Provided With A Parameter, Connects To The MS SQL DataBase And Verifies
                The Connection Via Running A SQL Query That Reverts The DataBase System Time.
            </para>
            <para type="description">
                Whenever The SQL Query Which Needs To Be Run Against The DataBase Is Too Complex Can Be Used SQL Query Input File.
                The CmdLet Does Allow Usage Of SQL Query File With Standard ".sql" Extension With Specifying Of SQL Parameters As
                HashTable, However It Does Not Support Usage Of Multiple ".sql" Files At Once. It Still Can Be Used To Import
                Single SQL Query And Using Of Collection Of SQL Parameter Dictionaries To Insert Multiple Rows At Once. The ".sql"
                File Read Happens In Begin Method, Which Exclude The Possibility To Execute Multiple SQL Queries At Once. I Don't
                Want File Operations In Process Method Because Of Performance Considerations.
            </para>
            <para type="description">
                Usage Of ".sql" Files To Keep The SQL Queries Out Of The Main Code Might Sound Like Heresy For The Normal
                Developers, Because The Queries Are Kept There In Clear Text And Can Be Modified By The User, Not To Forget About
                Possible Leak Of Proprietary Information. However This Is A PowerShell Module Which Means That It Will Be Used In
                Scripts And Automation On ".ps1" Files Which Are Clear Text As Well And The Users Have Access To The Code Inside.
                Therefore We Might Use The Opportunity To Have The Complex SQL Queries On Separate Files. Which Leads To The
                Conveniency Of Not Modifying Them Whenever The Next Version Is Released Or Modify Only Them Whenever Something On
                The DataBase Is Changed.
            </para>
            <para type="description">
                Because The Connection Is Established In The Begin Method It Is Done Once And All The Specified Queries Are Sent
                At Once In The Process Method Which Gain Huge Performance Boost.
            </para>
            <para type="description">
                To Boost The Performance Even Further, The CmdLet Will Execute All The Specified SQL Queries Or Single Query With
                Multiple Parameters Using Single SQL Transaction. The Using Of Transactions Happens After Evaluation Of The
                Specified SQL Query. Whenever The Query Contains The Keyword "COMMIT" Would Mean That The Transactions Are Managed
                Within The Query And The Transactions Functionality Of The CmdLet Is Always Disabled. In Order The CmdLet To Use
                The Transaction Functionality The SQL Query Must Contains Either "INSERT" Or "UPDATE" Keywords, And The
                Transactions Must Not Be Managed Within The Query Itself. It Is Always Preferable The User To Manage The
                Transactions Within The SQL Query, In That Way Not Leaving Any Chance Of The CmdLet Making Wrong Decisions.
            </para>
            <para type="description">
                The CmdLet Support Only Authenticated Connection The MS SQL Server Instance. There Are Three Possible Ways This To
                Be Achieved:
            </para>
            <para type="description">
                - Using The Windows Authentication - The CmdLet Uses The Windows Single Sign On To Connect To The DataBase Server
                Instance As The Currently Logged On User. It Is Preferable For Scripts Executed In Interactive Mode Which
                Eliminates The Need Of Additional Logon Steps. Whenever The Script Or The Automation Are Executed Unattended On
                Schedule As A Service This Might Lead To Unexpected Behavior. In Those Cases Is Preferable To Be Used Some Of The
                Next Authentication Options. Using This Option Can Be Authenticated Only Domain Or Windows Local Accounts.
            </para>
            <para type="description"> </para>
            <para type="description">
                - Via Specifying UserName And Password In Clear Text - In This Way, The UserName And The Password Are Included In
                The SQL Connection String, That Way Send Over The Network. In This Way, The User Relies Only On The Encryption Of
                The SQL Connection. Using This Method Impersonation Of The User That Connects To The DataBase Is Possible. Which
                Does Allow The Automations To Be Run With One Service Account And The DataBase Connection To Be Established With
                Different Service Account. Using This Option Can Be Authenticated Either Domain Or Windows Local Accounts Or SQL
                Users.
            </para>
            <para type="description">
                - Via Specifying [System.Management.Automation.PSCredential] Object. In This Way, The Password Is Not Kept In
                Clear Text. This Is Considered As More Secure. The Authentication Happens In The Same Way As The Windows
                Authenticates The Users. Therefore The Password Cannot Be Intercepted During The Authentication Even If The SQL
                Connection Encryption Is Already Breached. Using This Method Impersonation Of The User That Connects To The
                DataBase Is Possible. Which Does Allow The Automations To Be Run With One Service Account And The DataBase
                Connection To Be Established With Different Service Account. Using This Option Can Be Authenticated Only SQL Users.
            </para>
            <para type="description"> </para>
            <para type="description"> </para>
            <para type="description"> </para>
            <para type="description">
                The CmdLet Parameters And Pipeline Were Designed, Based On The Experience Gained On My Previous Work Related With
                SQLite And The Purpose Of This Module, Preserving The Parameter Names And Aliases And The Pipeline Functionalities
                Whenever It Is Possible. Design Decisions Related With The Pipeline Are Always Based On Compromises. Having In
                Mind The Usage Of The SQLite Module It Makes More Sense To Design The Pipeline In A Way, To Allow Running Multiple
                Queries Or Single Query With Multiple Sets Of SQL Parameters Against A Single DataBase, Rather Than Running Single
                Query With Single SQL Parameter Set Against Multiple DataBases. If I Ever Need High Availability On My Automations
                Would Prefer To Schedule Them To Run On A Virtual Machine Which Is A Resource On ESX Or Other Virtualization
                Platform Which Allows The Virtual Machine To Be Replicated And Powered On Different Hosts, Rather Than Writing A
                Script Capable To Jump From One Machine To Another And Maintaining The Same Information In Multiple DataBases.
            </para>
            <para type="description"> </para>
            <para type="description"> </para>
            <para type="description"> </para>
            <para type="description">
                With The Current Parameters And Pipeline Design The CmdLet Can:
            </para>
            <para type="description"> </para>
            <para type="description">
                - Single SQL Query Without Any SQL Parameters. The SQL Query Can Be Provided Directly To The "Query" Parameter Or
                As Object Property Matching The Parameter Name Or Parameter Aliases Of An Object Coming From The Pipeline.
                Although This Is The Easiest Way Of Working With DataBases It Is Not Recommended Because Of SQL Injection High
                Risk Unless Your SQL Queries Have No Users Input. Even In That Case There Is Still Possibility Of SQL Injection.
                For Example If You Use Dynamic Enumeration Of Objects Which Have To Be Inserted Into A DataBase And The Name Or
                Property Of A Dynamically Enumerated Object Have Value That Makes SQL Injection. Let’s Give A Specific Example If
                You Insert In A Table “ACCOUNTS” The Dynamically Enumerated Via LDAP Query Active Directory Accounts And Someone
                Created An Account With Name "Robert'); DROP TABLE [ACCOUNTS];--".
            </para>
            <para type="description">
                - Single SQL Query And Single SQL Parameters HashTable, Provided Directly To The Corresponding Parameters Or Via
                The Pipeline. Either The SQL Query And The SQL Parameters HashTable Provided As Properties Of An Object Coming
                From The Pipeline, Or The SQL Parameters HashTable Coming From The Pipeline And The SQL Query Specified In "Query"
                Parameter. Whenever SQL Parameters Are Used The Possibility Of SQL Injection Is Completely Eliminated.
            </para>
            <para type="description">
                - Single SQL Query And Collection Of SQL Parameters HashTables. In This Case The Parameters Represents Multiple
                Rows Which Have To Be Inserted Within Single Table. The User Have To Keep In Mind That The SQL Query Must Have SQL
                Variables For Each Of The Columns And Each HashTable In The Collections Have To Have Keys Corresponding To Each Of
                The SQL Variables. The SQL Query And Its Corresponding HashTables Collection Can Be Specified Either Directly To
                The Corresponding Parameters, Or As Properties Of An Object Coming From Pipeline, Or Specifying The Query To The
                CmdLet "Query" Parameter And The HashTables Collection Coming From The Pipeline.
            </para>
            <para type="description">
                The CmdLet Is Still Capable To Work With Collection Of SQL Queries, For That Purpose The Queries And Their
                Corresponding SQL Parameters HashTable / HashTables If Any, Can Be Provided Only Via Object Properties Of Objects
                Collection Coming From The Pipeline. Single Object Coming From The Pipeline Cannot Have Multiple SQL Queries.
            </para>
            <para type="description">
                The CmdLet Will Not Perform Any Normalization Of The Data Provided In The SQL Parameters Or Directly In The SQL
                Queries. It Is User Responsibility To Normalize The Data In The Way Which The MS SQL Server To Understand It. For
                Example All The Dates Must Be Present To The SQL Server As Strings Which Corresponds To The Column Data Type.
                Sending [System.DateTime] Object To The SQL Server Might Lead To Unexpected Behavior Or Errors. Even Though The
                Most Appropriate Way To Work With Dates Is To Convert Them To Long And Keep Them In Columns With Data Type BIGINT,
                But That Is A Personal Choice Simplifying The Developers Life.
            </para>
            <para type="description"> </para>
            <para type="description"> </para>
            <para type="description"> </para>
            <list type="alertSet">
                <item>
                    <term> </term>
                    <description>
                        <para>AUTHOR: Svetoslav Nedyalkov Savov</para>
                        <para>THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK</para>
                        <para>OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.</para>
                        <para></para>
                        <para></para>
                        <para></para>
                    </description>
                </item>
            </list>
            <example>
              <code>
                $strQry = "SELECT [name], [create_date], [collation_name], [state_desc] FROM [sys].[databases]";
                Invoke-SnsMsSqlQuery -Computer "DB01.contoso.com\Test" -DataBase "TestDB" -Query "$($strQry)" `
                    -UseCurrentLogOnSession -As "PSCustomObject";
              </code>
              <para> </para>
              <para> </para>
              <para> </para>
              <para>
                Initializes A SQL Query String Variable.
              </para>
              <para>
                Runs Single Query Against The MS SQL Server Instance About A Collection With The DataBases In It, Using The
                Currently Logged On User Security Context.
              </para>
              <para>
                The DataBase Parameter Is Required Because Of The Initial Catalog Property Of The SqlConnection String And Cannot
                Be Omitted. The "sys" Value Cannot Be Used.
              </para>
              <para> </para>
              <para> </para>
              <para> </para>
            </example>
            <example>
              <code>
                $objCred = Get-Credential;
                Invoke-SnsMsSqlQuery -Computer "DB01.contoso.com\Test" -DatabaseName "TestDB" -Credentials $objCred `
                -As "PSCustomObject";
              </code>
              <para> </para>
              <para> </para>
              <para> </para>
              <para>
                Asks The User To Specify Credentials And Store Them In Credential Object Variable.
              </para>
              <para>
                Runs Single Query Against The MS SQL Server Instance About A Collection With The Tables Within The Specified
                DataBase, Using PSCredential Object Created In The First Line.
              </para>
              <para>
                Because The Default Value Of "Query" Parameter Is The Query Needed To List The Tables, The Parameter Is Omitted.
              </para>
              <para>
                The Default Value In Query Parameter Contains &lt;DataBaseName&gt; Keyword, Which Indicates To The CmdLet To Replace It
                With The Value Specified In "DatabaseName" Parameter.
              </para>
              <para> </para>
              <para> </para>
              <para> </para>
            </example>
            <example>
              <code>
                $strQry = "SELECT * FROM [&lt;DataBaseName&gt;].[INFORMATION_SCHEMA].[COLUMNS] WHERE ";
                $strQry += "[TABLE_CATALOG] = '&lt;DataBaseName&gt;' AND [TABLE_SCHEMA] = 'dbo' AND [TABLE_NAME] = @Table";
                @{ "Table" = "TestTable"; } | Invoke-SnsMsSqlQuery -Computer "DB01.contoso.com\Test" -DatabaseName "TestDB" `
                    -Query "$($strQry)" -UserName "John.Smith" -Password "Pa$$w0rd" -As "PSCustomObject";
              </code>
              <para> </para>
              <para> </para>
              <para> </para>
              <para>
                Initializes A SQL Query String Variable, About Select All Columns From Specified Table Within The Specified
                Database.
              </para>
              <para>
                The SQL Query Contains &lt;DataBaseName&gt; Keyword, Which Indicates To The CmdLet To Replace It With The Value
                Specified In "DatabaseName" Parameter.
              </para>
              <para>
                Runs A Single Query Specified In The Previous Line, Against The Specified MS SQL Server Instance, Using UserName
                And Password Specified In Clear Text. Of Course The UserName And The Password In The Example Are Fake.
              </para>
              <para>
                The Table Is Specified As SQL Parameter Sent In A Form Of HashTable Via Pipeline Using "ValueFromPipeline".
              </para>
              <para> </para>
              <para> </para>
              <para> </para>
            </example>
            <example>
              <code>
                $strQry = "INSERT INTO [&lt;DataBaseName&gt;].[dbo].[Events] ([ID], [Message], [Severity], [Date])";
                $strQry += " VALUES (@ID, @Message, @Severity, @Date)";
                Invoke-SnsMsSqlQuery -Computer "DB01.contoso.com\Test" -DatabaseName "TestDB" -UseCurrentLogOnSession `
                    -Query "$($strQry)" -SqlParameters `
                    @{
                        "@ID" = 1;
                        "@Message" = "Some Dummy Message";
                        "Severity" = "Error";
                        "Date" = [System.DateTime]::Now.ToFileTime();
                    };
              </code>
              <para> </para>
              <para> </para>
              <para> </para>
              <para>
                Initializes "INSERT" Query Against The Specified Table In The Specified DataBase.
              </para>
              <para>
                Runs Single Query Against The Specified DataBase In The Specified MS SQL Server Instance, Using The Currently
                Logged On User Security Context.
              </para>
              <para>
                In The SQL Parameters Keys Are Used Both Strings With And Without At (@) Character. The Keys Match Exactly The SQL
                Query Variables. Please Note That There Are SQL Variables For Each Of The Columns.
              </para>
              <para> </para>
              <para>
                The "DateTime" Is Converted To "Long" And Inserted In A Column With Data Type "BIGINT". Using Long Integer To
                Store The Dates Gives As Certain Benefits, Like No Need To Keep Track Of The Time Zones, No Need To Keep Track Of
                The Daylight Saving Start And End Date And Whether It Is Applied To The Date. No Dependency From The Culture Set
                On The Computer Which Is Running The CmdLet And The Culture Set On The SQL Server. Additionally There Is No Lost
                In The Value Precision And No Rounding's. Reverse From "Long" To "DateTime" Object Is Just As Easy As The
                Conversion From "DateTime" To "Long".
              </para>
              <para>
                [System.DateTime]::FromFileTime(108666144000000000) - Converts The Long Integer To Local DateTime With Time Zone
                And Daylight Saving Taken Into Consideration.
              </para>
              <para>
                [System.DateTime]::FromFileTimeUtc(108666144000000000) - Converts The Long Integer To Universal DateTime. (Guess
                What Is The Date From The Example).
              </para>
              <para> </para>
              <para> </para>
              <para> </para>
            </example>
            <example>
              <code>
                Invoke-SnsMsSqlQuery -Computer "DB01.contoso.com\Test" -DatabaseName "TestDB" -UseCurrentLogOnSession `
                    -Query "INSERT INTO [&lt;DataBaseName&gt;].[dbo].[Computers] ([ID], [HostName]) VALUES (@ID, @HostName)" `
                    -SqlParameters `
                    @(
                        @{"@ID" = 1; "HostName" = "Computer1.contoso.com"; };
                        @{"@ID" = 2; "HostName" = "Computer2.contoso.com"; };
                    );
              </code>
              <para> </para>
              <para> </para>
              <para> </para>
              <para>
                Runs Single "INSERT" Query Against The Specified Table In The Specified DataBase In The Specified MS SQL Server
                Instance With Multiple SQL Parameters HashTables Using The Currently Logged On User Security Context. Because
                There Are Two HashTables In The SQL Parameters Collection This Creates Two Rows / Entries In The Destination
                Table. This Is Very Useful Whenever Large Amount Of Data Have To Be Inserted Into The DataBase.
              </para>
              <para>
                In The SQL Parameters Keys Are Used Both Strings With And Without At (@) Character. The Keys Match Exactly The SQL
                Query Variables. Please Note That There Are SQL Variables For Each Of The Columns.
              </para>
              <para> </para>
              <para> </para>
              <para> </para>
            </example>
            <example>
              <code>
                [System.Object[]]$arrInput = @();
                 
                [System.Object]$objObject = New-Object -TypeName "System.Object";
                $objObject | Add-Member -Force -MemberType "NoteProperty" -Name "ID" -Value 1;
                $objObject | Add-Member -Force -MemberType "NoteProperty" -Name "Event" -Value "Warning";
                $objObject | Add-Member -Force -MemberType "NoteProperty" -Name "Date" -Value([System.DateTime]::Now.ToFileTime());
                [System.Object[]]$arrInput += $objObject;
                 
                [System.Object]$objObject = New-Object -TypeName "System.Object";
                $objObject | Add-Member -Force -MemberType "NoteProperty" -Name "ID" -Value 2;
                $objObject | Add-Member -Force -MemberType "NoteProperty" -Name "Event" -Value "Error";
                $objObject | Add-Member -Force -MemberType "NoteProperty" -Name "Date" -Value([System.DateTime]::Now.ToFileTime());
                [System.Object[]]$arrInput += $objObject;
                 
                [SnsMsSqlPsModule.PsObjectToHashTbl]::ToHashTbl($($arrInput | Select-Object *)) | `
                    Invoke-SnsMsSqlQuery -Computer "DB01.contoso.com\Test" -DatabaseName "TestDB" -UseCurrentLogOnSession `
                    -Query "INSERT INTO [&lt;DataBaseName&gt;].[dbo].[Events] ([ID], [Event], [Date]) VALUES (@ID, @Event, @Date)";
              </code>
              <para> </para>
              <para> </para>
              <para> </para>
              <para>
                Initializes A Collection With Custom Objects Which Are Not As Easily Converted To HashTables As The
                "PSCustomObject".
              </para>
              <para>
                Converts The Custom Objects From The Previous Line To HashTable Collection And Send It To The Pipeline. For That
                Purpose We Are Using A .NET Method Made Publicly Available Which Is Present In The Current PowerShell Module.
              </para>
              <para>
                Runs Single "INSERT" Query Against The Specified Table In The Specified DataBase In The Specified MS SQL Server
                Instance With Multiple SQL Parameters HashTables Coming From The Pipeline Using The Currently Logged On User
                Security Context. Because There Are Two HashTables In The SQL Parameters Collection This Creates Two Rows /
                Entries In The Destination Table.
              </para>
              <para>
                This Is Useful Whenever We Need To Perform Bulk Inserts Or Updates In A Single Transaction Of Large Amount Of
                Data, Coming On A Collection Of All Kinds Of Classes, Including Imported From A File Using Import-Csv CmdLet. The
                Example Uses A Collection With Just Two Objects For Readability.
              </para>
              <para>
                In Order The Bulk Operations To Work Smoothly And To Avoid Errors:
              </para>
              <para>
                - The Input Objects Property Names Must Match Exactly The SQL Variable Names In The SQL Query.
              </para>
              <para>
                - The Input Objects Property Value Types Must Match Exactly The Corresponding Columns Data Types.
              </para>
              <para>
                - Any Input Object Property Values Must Be Normalized In Advance.
              </para>
              <para> </para>
              <para> </para>
              <para> </para>
            </example>
            </summary>
            <para type="link" uri="https://github.com/svesavov/SnsMsSqlPsModule"> svesavov / SnsMsSqlPsModule - </para>
            <para type="link" uri="https://www.powershellgallery.com/packages/SnsMsSqlPsModule/"> PowerShell Gallery - </para>
            <para type="link" uri="https://www.linkedin.com/in/svetoslavsavov"> Svetoslav Savov on LinkedIn - </para>
            <para type="link" uri="https://www.microsoft.com/en-us/sql-server/sql-server-downloads"> MS SQL Server Download - </para>
            <para type="link" uri="https://docs.microsoft.com/en-us/sql/t-sql/data-types/data-types-transact-sql"> MS SQL Data Types - </para>
            <para type="link" uri="https://docs.microsoft.com/en-us/sql/t-sql/language-reference"> MS SQL Supported SQL Syntax - </para>
            <para type="link" uri="https://www.sqlservertutorial.net/"> MS SQL Tutorials - </para>
        </member>
        <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlQuery.Computer">
            <summary>
            <para type="description">Specifies A MS SQL Server And MS SQL Instance In Format:</para>
            <para type="description">"DataBase Server"</para>
            <para type="description">"DataBase Server"\"DataBase Instance"</para>
            <para type="description">"DataBase Server","Port Number"</para>
            </summary>
        </member>
        <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlQuery.DatabaseName">
            <summary>
            <para type="description">Specifies A DataBase Name</para>
            </summary>
        </member>
        <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlQuery.UserName">
            <summary>
            <para type="description">Specifies A UserName In Clear Text</para>
            </summary>
        </member>
        <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlQuery.Password">
            <summary>
            <para type="description">Specifies A [System.Security.SecureString] Or [System.String] Password To Use In The MS SQL Connection.</para>
            </summary>
        </member>
        <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlQuery.Credentials">
            <summary>
            <para type="description">Specifies A [System.Management.Automation.PSCredential] Object</para>
            </summary>
        </member>
        <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlQuery.UseCurrentLogOnSession">
            <summary>
            <para type="description">Specifies That Current User LogOnSession Will Be Used For The SQL Connection</para>
            </summary>
        </member>
        <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlQuery.ConnectionString">
            <summary>
            <para type="description">Specifies An SQL Connection String</para>
            </summary>
        </member>
        <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlQuery.Query">
            <summary>
            <para type="description">Specifies The SQL Query To Be Run.</para>
            </summary>
        </member>
        <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlQuery.SqlParameters">
            <summary>
            <para type="description">HashTable Of Parameters For Parameterized SQL Queries.</para>
            <para type="description">The HashTable Keys Must Match The SQL Query Variable Names.</para>
            <para type="description">The Keys Might Be Specified Either With Or Without At (@) Character In Front.</para>
            <para type="description">The CmdLet Will Take Care To Add Them If Missing.</para>
            <para type="description">Example:</para>
            <para type="description">-Query "SELECT * FROM [db].[dbo].[tblServers] WHERE [ServerFqdn] LIKE @Server"</para>
            <para type="description">-SqlParameters @{"@Server" = "Server01.contoso.com"}</para>
            </summary>
        </member>
        <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlQuery.InputFile">
            <summary>
            <para type="description">Specifies A ".sql" File To Be Used As Query Input.</para>
            <para type="description">Works Only With Full Absolute UNC Path To The File.</para>
            </summary>
        </member>
        <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlQuery.QueryTimeout">
            <summary>
            <para type="description">Sets The Wait Time In Seconds Before Terminating The Attempt To Execute A Command And Generating An Error</para>
            <para type="description">Use 0 For The Default TimeOut Value In .NET And MS SQL Server</para>
            </summary>
        </member>
        <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlQuery.As">
            <summary>
            <para type="description">Specifies To The CmdLet To Revert The Output In One Of The Following Types:</para>
            <para type="description">[System.Data.DataSet]</para>
            <para type="description">[System.Data.DataTable]</para>
            <para type="description">[System.Data.DataRow]</para>
            <para type="description">[System.Management.Automation.PSCustomObject]</para>
            </summary>
        </member>
    </members>
</doc>