## Microsoft Function Naming Convention: http://msdn.microsoft.com/en-us/library/ms714428(v=vs.85).aspx #region Function Invoke-SQLDBQuery Function Invoke-SQLDBQuery { <# .SYNOPSIS This function is able to execute one or more queries against an the specified Microsoft SQL database using either Windows or SQL authentication. .DESCRIPTION The database connection will be opened, each database query will be executed, the results will be stored, and the database connection will be closed. .PARAMETER Server .PARAMETER Instance .PARAMETER Port .PARAMETER UserID .PARAMETER Password .PARAMETER Database .PARAMETER DBQueryList .EXAMPLE $DBQueryResults = Invoke-SQLDBQuery -Server 'MyDBServer.mydomain.com' -UserID 'SQLAuthUser' -Password 'SQLAuthUserPassword' -Database 'YourDatabaseName' -DBQueryList @('Select * From YourTableName') -Verbose Write-Output -InputObject ($DBQueryResults) .EXAMPLE $DBQueryList = [System.Collections.Generic.List[String]]@() $DBQueryList.Add(@" Select Distinct dbo.v_Add_Remove_Programs.DisplayName0 As 'DisplayName' From dbo.v_Add_Remove_Programs Order By dbo.v_Add_Remove_Programs.DisplayName0 "@) $DBQueryResults = Invoke-SQLDBQuery -Server 'MyDBServer.mydomain.com' -Database 'YourDatabaseName' -DBQueryList ($DBQueryList) -Verbose .EXAMPLE $DBQueryResults = Invoke-SQLDBQuery -Server 'MyDBServer.mydomain.com' -Instance 'MyDBInstance' -Database 'YourDatabaseName' -DBQueryList @('Select * From YourTableName') Write-Output -InputObject ($DBQueryResults) .EXAMPLE $DBQueryResults = Invoke-SQLDBQuery -Server 'MyDBServer.mydomain.com' -Port '4530' -Database 'YourDatabaseName' -DBQueryList @('Select * From YourTableName1', 'Select * From YourTableName2') Write-Output -InputObject ($DBQueryResults) .LINK https://www.SQLshack.com/connecting-powershell-to-SQL-server/ .LINK https://stackoverflow.com/questions/64681003/fetch-a-whole-row-from-SQL-server-with-powershell #> [CmdletBinding(ConfirmImpact = 'Low', HelpURI = '', DefaultParameterSetName = '_AllParameterSets', SupportsShouldProcess = $True, PositionalBinding = $True)] Param ( [Parameter(Mandatory=$True)] [ValidateNotNullOrEmpty()] [String]$Server, [Parameter(Mandatory=$False)] [ValidateNotNullOrEmpty()] [String]$Instance, [Parameter(Mandatory=$False)] [ValidateNotNullOrEmpty()] [String]$Port, [Parameter(Mandatory=$False)] [ValidateNotNullOrEmpty()] [String]$UserID, [Parameter(Mandatory=$False)] [ValidateNotNullOrEmpty()] [String]$Password, [Parameter(Mandatory=$False)] [ValidateNotNullOrEmpty()] [String]$Database, [Parameter(Mandatory=$True)] [ValidateNotNullOrEmpty()] [String[]]$DBQueryList, [Parameter(Mandatory=$False, ParameterSetName = 'Export')] [Switch]$ExportJSON, [Parameter(Mandatory=$False, ParameterSetName = 'Export')] [System.IO.DirectoryInfo]$ExportDirectory, [Parameter(Mandatory=$False)] [Switch]$ContinueOnError ) Begin { [ScriptBlock]$ErrorHandlingDefinition = { If ([String]::IsNullOrEmpty($_.Exception.Message)) {$ExceptionMessage = "$($_.Exception.Errors.Message -Join "`r`n`r`n")"} Else {$ExceptionMessage = "$($_.Exception.Message)"} [String]$ErrorMessage = "[Error Message: $($ExceptionMessage)]`r`n`r`n[ScriptName: $($_.InvocationInfo.ScriptName)]`r`n[Line Number: $($_.InvocationInfo.ScriptLineNumber)]`r`n[Line Position: $($_.InvocationInfo.OffsetInLine)]`r`n[Code: $($_.InvocationInfo.Line.Trim())]" If ($ContinueOnError.IsPresent -eq $True) { Write-Warning -Message ($ErrorMessage) } ElseIf ($ContinueOnError.IsPresent -eq $False) { Throw ($ErrorMessage) } } Try { $DateTimeMessageFormat = 'MM/dd/yyyy HH:mm:ss.FFF' ###03/23/2022 11:12:48.347### [ScriptBlock]$GetCurrentDateTimeMessageFormat = {(Get-Date).ToString($DateTimeMessageFormat)} $DateTimeLogFormat = 'dddd, MMMM dd, yyyy @ hh:mm:ss.FFF tt' ###Monday, January 01, 2019 @ 10:15:34.000 AM### [ScriptBlock]$GetCurrentDateTimeLogFormat = {(Get-Date).ToString($DateTimeLogFormat)} $DateFileFormat = 'yyyyMMdd' ###20190403### [ScriptBlock]$GetCurrentDateFileFormat = {(Get-Date).ToString($DateFileFormat)} $DateTimeFileFormat = 'yyyyMMdd_HHmmss' ###20190403_115354### [ScriptBlock]$GetCurrentDateTimeFileFormat = {(Get-Date).ToString($DateTimeFileFormat)} $TextInfo = (Get-Culture).TextInfo #Determine the date and time we executed the function $FunctionStartTime = (Get-Date) [String]$CmdletName = $MyInvocation.MyCommand.Name $LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Function `'$($CmdletName)`' is beginning. Please Wait..." Write-Verbose -Message $LogMessage #Define Default Action Preferences $ErrorActionPreference = 'Stop' $LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The following parameters and values were provided to the `'$($CmdletName)`' function." Write-Verbose -Message $LogMessage $FunctionProperties = Get-Command -Name $CmdletName $FunctionParameters = $FunctionProperties.Parameters.Keys | Where-Object {($_ -inotmatch '(^Password$)|(^UserID$)|(^DBQueryList$)')} ForEach ($Parameter In $FunctionParameters) { If (!([String]::IsNullOrEmpty($Parameter))) { $ParameterProperties = Get-Variable -Name $Parameter -ErrorAction SilentlyContinue $ParameterValueCount = $ParameterProperties.Value | Measure-Object | Select-Object -ExpandProperty Count If ($ParameterValueCount -gt 1) { $ParameterValueStringFormat = ($ParameterProperties.Value | ForEach-Object {"`"$($_)`""}) -Join ", " $LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($ParameterProperties.Name):`r`n`r`n$($ParameterValueStringFormat)" } Else { $ParameterValueStringFormat = ($ParameterProperties.Value | ForEach-Object {"`"$($_)`""}) -Join ', ' $LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($ParameterProperties.Name): $($ParameterValueStringFormat)" } If (!([String]::IsNullOrEmpty($ParameterProperties.Name))) { Write-Verbose -Message $LogMessage } } } $LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Execution of $($CmdletName) began on $($FunctionStartTime.ToString($DateTimeLogFormat))" Write-Verbose -Message $LogMessage #Create an array list that will contain the functions output $OutputObjectProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' #Define Variable(s) $LoggingDetails = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' $LoggingDetails.Add('LogMessage', $Null) $LoggingDetails.Add('WarningMessage', $Null) $LoggingDetails.Add('ErrorMessage', $Null) #Create a database connection string builder $DBConnectionStringBuilder = [System.Data.SQLClient.SQLConnectionStringBuilder]::New() #Create a database data source builder $DataSourceBuilder = [System.Text.StringBuilder]::New() #Dynamically build the connection string based on parameter values Switch ($True) { {([String]::IsNullOrEmpty($Server) -eq $False) -and ([String]::IsNullOrWhiteSpace($Server) -eq $False)} { $Null = $DataSourceBuilder.Append($Server) } {([String]::IsNullOrEmpty($Instance) -eq $False) -and ([String]::IsNullOrWhiteSpace($Instance) -eq $False)} { $Null = $DataSourceBuilder.Append("\$($Instance)") } {([String]::IsNullOrEmpty($Port) -eq $False) -and ([String]::IsNullOrWhiteSpace($Port) -eq $False)} { $Null = $DataSourceBuilder.Append(",$($Port)") } {([String]::IsNullOrEmpty($Database) -eq $False) -and ([String]::IsNullOrWhiteSpace($Database) -eq $False)} { $DBConnectionStringBuilder.PSBase.InitialCatalog = $Database } {([String]::IsNullOrEmpty($ExportDirectory) -eq $True) -and ([String]::IsNullOrWhiteSpace($ExportDirectory) -eq $True)} { [System.IO.DirectoryInfo]$ExportDirectory = "$($Env:Public)\Documents\Reports\$($CmdletName)" } } $DBConnection = [System.Data.SQLClient.SQLConnection]::New() Switch (([String]::IsNullOrEmpty($UserID) -eq $True) -or ([String]::IsNullOrWhiteSpace($UserID) -eq $True) -or ([String]::IsNullOrEmpty($Password) -eq $True) -or ([String]::IsNullOrWhiteSpace($Password) -eq $True)) { {($_ -eq $True)} { $DBConnectionStringBuilder.PSBase.IntegratedSecurity = $True } Default { $DBConnectionStringBuilder.PSBase.IntegratedSecurity = $False $Credential = [System.Management.Automation.PSCredential]::New($UserID, (ConvertTo-SecureString -String $Password -AsPlainText -Force)) $Null = $Credential.Password.MakeReadOnly() $DBConnectionCredential = [System.Data.SQLClient.SQLCredential]::New($Credential.UserName, $Credential.Password) $DBConnection.Credential = ($DBConnectionCredential) } } $DBConnectionStringBuilder.PSBase.DataSource = $DataSourceBuilder.ToString() $DBConnectionString = $DBConnectionStringBuilder.ConnectionString $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - " + "Database Connection String = $($DBConnectionString)" Write-Verbose -Message ($LoggingDetails.LogMessage) $DBConnection.ConnectionString = ($DBConnectionString) $LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to open the database connection. Please Wait..." Write-Verbose -Message $LogMessage $DBConnection.Open() } Catch { $ErrorHandlingDefinition.Invoke() } } Process { For ($DBQueryListIndex = 0; $DBQueryListIndex -lt $DBQueryList.Count; $DBQueryListIndex++) { Try { [String]$DBQuery = $DBQueryList[$DBQueryListIndex] [Int]$DBQueryNumber = $DBQueryListIndex + 1 $DBCommand = [System.Data.SQLClient.SQLCommand]::New() $DBCommand.Connection = ($DBConnection) $DBCommand.CommandText = ($DBQuery) $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - " + "Attempting to execute database query #$($DBQueryNumber). Please Wait..." Write-Verbose -Message ($LoggingDetails.LogMessage) $Null = $DBCommand.ExecuteScalar() $DBQueryResults = [System.Data.DataTable]::New() $DBDataAdapter = [System.Data.SQLClient.SQLDataAdapter]::New($DBCommand) $Null = $DBDataAdapter.Fill($DBQueryResults) $DBQueryRowCount = $DBQueryResults.Rows.Count $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - " + "$($DBQueryRowCount) row(s) were returned from the database query." Write-Verbose -Message ($LoggingDetails.LogMessage) Switch ($DBQueryRowCount -gt 0) { {($_ -eq $True)} { $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - " + "Database Query Column Names = $($DBQueryResults.Columns.ColumnName -Join ', ')" Write-Verbose -Message ($LoggingDetails.LogMessage) } } [String]$ResultSetNumber = ($DBQueryNumber).ToString('000') [String]$ResultSetPropertyName = "ResultSet$($ResultSetNumber)" $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - " + "Attempting to add the result(s) from database query #$($DBQueryNumber) to property `"$($ResultSetPropertyName)`". Please Wait..." Write-Verbose -Message ($LoggingDetails.LogMessage) $PropertyInclusionList = [System.Collections.Generic.List[Object]]@() $Null = $PropertyInclusionList.Add('*') $PropertyExclusionList = [System.Collections.Generic.List[Object]]@() $Null = $PropertyExclusionList.AddRange(('HasErrors', 'ItemArray', 'RowError', 'RowState', 'Table')) $DBQueryRows = $DBQueryResults.Rows | Select-Object -Property ($PropertyInclusionList) -ExcludeProperty ($PropertyExclusionList) $Null = $OutputObjectProperties.Add($ResultSetPropertyName, $DBQueryRows) } Catch { $ErrorHandlingDefinition.Invoke() } Finally { Try { $Null = $DBQueryResults.Dispose() $Null = $DBDataAdapter.Dispose() } Catch { } } } } End { Try { #Close the database connection $LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to close the database connection. Please Wait..." Write-Verbose -Message $LogMessage $Null = $DBConnection.Close() #Create the powershell object that will be exported to the powershell pipeline $OutputObject = New-Object -TypeName 'PSObject' -Property ($OutputObjectProperties) #If specified, export each result set to a separate JSON file Switch ($True) { {($ExportJSON.IsPresent)} { $ExportParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' $ExportParameters.Add('Compress', $False) $ExportParameters.Add('Encoding', [System.Text.Encoding]::Default) $ExportParameters.Add('Depth', 10) [String[]]$OutputObjectPropertyList = $OutputObject.PSObject.Properties.Name For ($OutputObjectPropertyListIndex = 0; $OutputObjectPropertyListIndex -lt $OutputObjectPropertyList.Count; $OutputObjectPropertyListIndex++) { [String]$OutputObjectPropertyName = $OutputObjectPropertyList[$OutputObjectPropertyListIndex] [System.IO.FileInfo]$OutputObjectExportPath = "$($ExportDirectory.FullName)\$($OutputObjectPropertyName).json" Switch ([System.IO.File]::Exists($OutputObjectExportPath.FullName)) { {($_ -eq $True)} { [String]$JSONContents = [System.IO.File]::ReadAllText($OutputObjectExportPath.FullName, $ExportParameters.Encoding) $JSONObject = ConvertFrom-JSON -InputObject ($JSONContents) $OutputObjectPropertyValue = $OutputObject.$($OutputObjectPropertyName) $JSONObject.$($OutputObjectPropertyName) = ($OutputObjectPropertyValue) [String]$OutputObjectPropertyValueAsJSON = $JSONObject | ConvertTo-JSON -Depth ($ExportParameters.Depth) -Compress:($ExportParameters.Compress) $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - " + "Attempting to update the result set `"$($OutputObjectPropertyName)`" within the file of `"$($OutputObjectExportPath.FullName)`". Please Wait..." Write-Verbose -Message ($LoggingDetails.LogMessage) $Null = [System.IO.File]::WriteAllText($OutputObjectExportPath.FullName, $OutputObjectPropertyValueAsJSON, $ExportParameters.Encoding) } {($_ -eq $False)} { $OutputObjectPropertyValue = $OutputObject | Select-Object -Property @($OutputObjectPropertyName) [String]$OutputObjectPropertyValueAsJSON = $OutputObjectPropertyValue | ConvertTo-JSON -Depth ($ExportParameters.Depth) -Compress:($ExportParameters.Compress) Switch ([System.IO.Directory]::Exists($OutputObjectExportPath.Directory.FullName)) { {($_ -eq $False)} { $Null = [System.IO.Directory]::CreateDirectory($OutputObjectExportPath.Directory.FullName) } } $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - " + "Attempting to export the result set `"$($OutputObjectPropertyName)`" to the file of `"$($OutputObjectExportPath.FullName)`". Please Wait..." Write-Verbose -Message ($LoggingDetails.LogMessage) $Null = [System.IO.File]::WriteAllText($OutputObjectExportPath.FullName, $OutputObjectPropertyValueAsJSON, $ExportParameters.Encoding) } } } } } #Write the object to the powershell pipeline Write-Output -InputObject ($OutputObject) #Determine the date and time the function completed execution $FunctionEndTime = (Get-Date) $LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Execution of $($CmdletName) ended on $($FunctionEndTime.ToString($DateTimeLogFormat))" Write-Verbose -Message $LogMessage #Log the total script execution time $FunctionExecutionTimespan = New-TimeSpan -Start ($FunctionStartTime) -End ($FunctionEndTime) $LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Function execution took $($FunctionExecutionTimespan.Hours.ToString()) hour(s), $($FunctionExecutionTimespan.Minutes.ToString()) minute(s), $($FunctionExecutionTimespan.Seconds.ToString()) second(s), and $($FunctionExecutionTimespan.Milliseconds.ToString()) millisecond(s)" Write-Verbose -Message $LogMessage $LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Function `'$($CmdletName)`' is completed." Write-Verbose -Message $LogMessage } Catch { $ErrorHandlingDefinition.Invoke() } } } #endregion