Help with database query script

I am running into trouble getting my script to work. I am trying to create a script that will query a database, using Azure Automation, and create a file with a list of items that the query has found. The issue I am running into is that it does not seem to be taking some of my parameters and putting them into variables. If I run the command in the script, with parameters that I am setting, I get the results that I expected except for the blank line at the top of the output file. If I try to run the script the way it is supposed to be used, I get the error message below.

Directory Exists!
Transcript started, output file is C:\Scripts\Results\Database\Query-Database Results.Tue 02-15-2022 12-03 PM.log
Out-File : Cannot bind argument to parameter ‘FilePath’ because it is an empty string.
At C:\Users\a03.Tom_Bingeman\Query-Database.ps1:111 char:161

  • … -Wrap -HideTableHeaders | Select-Object -Skip 1 | Out-File “$Output”
  •                                                             ~~~~~~~~~
    
    • CategoryInfo : InvalidData: (:slight_smile: [Out-File], ParameterBindingValidationException
    • FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Microsoft.PowerShell.Commands.OutF
      ileCommand

Transcript stopped, output file is C:\Scripts\Results\Database\Query-Database Results.Tue 02-15-2022 12-03 PM.log

I am not sure where I am going wrong as I am not an expert at using PowerShell. Any help that someone could provide would be greatly appreciated.

[CmdLetBinding()]
param(
	[Parameter(Mandatory=$True, HelpMessage="Path to SQL Query file")]
    [string]$SQLQueryFile = $null,
	[Parameter(Mandatory=$True, HelpMessage="Name of SQL Server")]
    [array]$SQLServer = $null,
    [Parameter(Mandatory=$False, HelpMessage="Name of SQL Instance on the server")]
    [array]$SQLInstance = $null,
    [Parameter(Mandatory=$True, HelpMessage="Path to place the output file")]
    [array]$OutputFilePath = $null
)

# Set the file that has the SQL Query in it
if ($SQLQueryFile)
{
    try
    {
        Test-Path $SQLQueryFile -ErrorAction Stop | Out-Null
    }
    catch
    {
        $ErrorMessage = "[ERROR] Missing SQL Query File file -SQLQueryFile $SQLQueryFile"
        Write-Host "$ErrorMessage"
        exit
    }
    
    # Make sure you can import the query from the file.
    try
    {
        $SQLQuery = $SQLQueryFile
    }
    catch
    {
        $ErrorMessage = "[ERROR] Error importing SQL Query from file: $($SQLQueryFile)"
        exit
    }
}
#Set the SQL server name.
elseif ($SQLServer)
{
    $SQLServer
}
#Set the path to the output file you want.
elseif ($OutputFilePath)
{
    $Output = $Output.tostring($OutputFilePath)
    $Output
}
#Set the SQL instance of the SQL server if there is multiple instances on the SQL server.
elseif ($SQLInstance)
{
    $Instance = \$SQLInstance
}

#Create function for logging timestamps.
function Get-TimeStamp {
    
    return "[{0:MM/dd/yy} {0:HH:mm:ss}]" -f (Get-Date)
    
}

#Set up the folders and files needed for logging the script.
$LogFolder = "C:\Scripts\Results\Database"
$Exists = Test-Path -Path $LogFolder
if ($Exists -eq "True") {
    Write-Host "Directory Exists!"
} else {
    New-Item -Path $LogFolder -ItemType directory
}

$logdate = (Get-Date -UFormat ".%a %m-%d-%Y %H-%M %p")
$logfile = new-item -path $LogFolder -Name "Query-Database Results$logdate.log"

start-Transcript -path $Logfile

#Run the commands to perform the SQL query.
Invoke-Sqlcmd -InputFile "$SQLQuery" -ServerInstance "$SQLServer$Instance"  | Format-Table -AutoSize -Wrap -HideTableHeaders | Select-Object -Skip 1 | Out-File "$Output"

Stop-Transcript

First issue that stands out is using Format-Table. All the Format-* cmdlets are intended to be output for user consumption only. They destroy the objects so you can’t process them further down the pipeline like you’re trying with select-Object. Try removing the format-table command chunk

1 Like

krzydoug,

Thank you for your suggestion but even when I remove that, the variable is not being filled in. This is why I believe it has something to do with my parameters somehow.

Might be a typo, but why are all your variables quoted? No way for me to test, just thinking out loud.

The variables are in the command with quotes in case of a space in the path to the file. I could probably remove the one from -ServerInstance but the $Instance variable does have a \ in the variable.

It wasn’t an opinion, do not use format commands if you intend to use the objects they destroy.

Here is an example

$service = Get-Service wuauserv

$service | Select-Object -Property Name

Name    
----    
wuauserv

Compare to

$service = Get-Service wuauserv

$service | Format-Table | Select-Object -Property Name

Name
----

It’s even more apparent when you convert to something like csv

First, not destroyed object

$service | ConvertTo-Csv -NoTypeInformation

"Name","RequiredServices","CanPauseAndContinue","CanShutdown","CanStop","DisplayName","DependentServices","MachineName","ServiceName","ServicesDependedOn","ServiceHandle","Status","ServiceTyp
e","StartType","Site","Container"
"wuauserv","System.ServiceProcess.ServiceController[]","False","False","True","Windows Update","System.ServiceProcess.ServiceController[]",".","wuauserv","System.ServiceProcess.ServiceControl
ler[]","SafeServiceHandle","Running","Win32OwnProcess, Win32ShareProcess","Automatic",,

Now a formatting destroyed object

$service | Format-Table | ConvertTo-Csv -NoTypeInformation

"ClassId2e4f51ef21dd47e99d3c952918aff9cd","pageHeaderEntry","pageFooterEntry","autosizeInfo","shapeInfo","groupingEntry"
"033ecb2bc07a4d43b5ef94ed5a35d280",,,,"Microsoft.PowerShell.Commands.Internal.Format.TableHeaderInfo",
"9e210fe47d09416682b841769c78b8a3",,,,,
"27c87ef9bbda4f709f6b4002fa4af63c",,,,,
"4ec4f0187cb04f4cb6973460dfe252df",,,,,
"cf522b78d86c486691226b40aa69e95c",,,,,

And to see even more about what’s actually going on

Nice object we can process down the pipeline

$service | Get-Member

   TypeName: System.ServiceProcess.ServiceController

No longer an object that we can process

$service | Format-Table | Get-Member

   TypeName: Microsoft.PowerShell.Commands.Internal.Format.FormatStartData

Name                                    MemberType Definition                                                                                                                                 
----                                    ---------- ----------                                                                                                                                 
Equals                                  Method     bool Equals(System.Object obj)                                                                                                             
GetHashCode                             Method     int GetHashCode()                                                                                                                          
GetType                                 Method     type GetType()                                                                                                                             
ToString                                Method     string ToString()                                                                                                                          
autosizeInfo                            Property   Microsoft.PowerShell.Commands.Internal.Format.AutosizeInfo, System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyTok...
ClassId2e4f51ef21dd47e99d3c952918aff9cd Property   string ClassId2e4f51ef21dd47e99d3c952918aff9cd {get;}                                                                                      
groupingEntry                           Property   Microsoft.PowerShell.Commands.Internal.Format.GroupingEntry, System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyTo...
pageFooterEntry                         Property   Microsoft.PowerShell.Commands.Internal.Format.PageFooterEntry, System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKey...
pageHeaderEntry                         Property   Microsoft.PowerShell.Commands.Internal.Format.PageHeaderEntry, System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKey...
shapeInfo                               Property   Microsoft.PowerShell.Commands.Internal.Format.ShapeInfo, System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=...


   TypeName: Microsoft.PowerShell.Commands.Internal.Format.GroupStartData

Name                                    MemberType Definition                                                                                                                                 
----                                    ---------- ----------                                                                                                                                 
Equals                                  Method     bool Equals(System.Object obj)                                                                                                             
GetHashCode                             Method     int GetHashCode()                                                                                                                          
GetType                                 Method     type GetType()                                                                                                                             
ToString                                Method     string ToString()                                                                                                                          
ClassId2e4f51ef21dd47e99d3c952918aff9cd Property   string ClassId2e4f51ef21dd47e99d3c952918aff9cd {get;}                                                                                      
groupingEntry                           Property   Microsoft.PowerShell.Commands.Internal.Format.GroupingEntry, System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyTo...
shapeInfo                               Property   Microsoft.PowerShell.Commands.Internal.Format.ShapeInfo, System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=...


   TypeName: Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData

Name                                    MemberType Definition                                                                                                                                 
----                                    ---------- ----------                                                                                                                                 
Equals                                  Method     bool Equals(System.Object obj)                                                                                                             
GetHashCode                             Method     int GetHashCode()                                                                                                                          
GetType                                 Method     type GetType()                                                                                                                             
ToString                                Method     string ToString()                                                                                                                          
ClassId2e4f51ef21dd47e99d3c952918aff9cd Property   string ClassId2e4f51ef21dd47e99d3c952918aff9cd {get;}                                                                                      
formatEntryInfo                         Property   Microsoft.PowerShell.Commands.Internal.Format.FormatEntryInfo, System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKey...
outOfBand                               Property   bool outOfBand {get;set;}                                                                                                                  
writeStream                             Property   Microsoft.PowerShell.Commands.Internal.Format.WriteStreamType, System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKey...


   TypeName: Microsoft.PowerShell.Commands.Internal.Format.GroupEndData

Name                                    MemberType Definition                                                                                                                                 
----                                    ---------- ----------                                                                                                                                 
Equals                                  Method     bool Equals(System.Object obj)                                                                                                             
GetHashCode                             Method     int GetHashCode()                                                                                                                          
GetType                                 Method     type GetType()                                                                                                                             
ToString                                Method     string ToString()                                                                                                                          
ClassId2e4f51ef21dd47e99d3c952918aff9cd Property   string ClassId2e4f51ef21dd47e99d3c952918aff9cd {get;}                                                                                      
groupingEntry                           Property   Microsoft.PowerShell.Commands.Internal.Format.GroupingEntry, System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyTo...


   TypeName: Microsoft.PowerShell.Commands.Internal.Format.FormatEndData

Name                                    MemberType Definition                                                                                                                                 
----                                    ---------- ----------                                                                                                                                 
Equals                                  Method     bool Equals(System.Object obj)                                                                                                             
GetHashCode                             Method     int GetHashCode()                                                                                                                          
GetType                                 Method     type GetType()                                                                                                                             
ToString                                Method     string ToString()                                                                                                                          
ClassId2e4f51ef21dd47e99d3c952918aff9cd Property   string ClassId2e4f51ef21dd47e99d3c952918aff9cd {get;}                                                                                      
groupingEntry                           Property   Microsoft.PowerShell.Commands.Internal.Format.GroupingEntry, System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyTo...

There is no need to manipulate it into a string formatted table and remove the headers.

2 Likes

Hi, welcome to the forum :wave:

I think you’ve misunderstood the logic flow.

Your test if (SQLQueryFile) is true when the parameter is provided. That means your elseif tests will never be evalutated, elseif is only evaluated if the preceding if evaluates to false:

if ($true) {
    Write-Host 'Let the truth be known.'
}
elseif ($true) {
    Write-Host 'You will not see this'
}

if ($false) {
    Write-Host 'Let the truth be known.'
}
elseif ($true) {
    Write-Host 'Entered the elseif()'
}

elseif aside, this won’t work because $Output has no value so you can’t call the ToString() method on it.

elseif ($OutputFilePath)
{
    $Output = $Output.tostring($OutputFilePath)
    $Output
}

Why not just use $OutputFilePath directly?

Out-File $OutputFilePath

As all the parameters are mandatory, I would not bother setting them to $null and then checking if they have a value in the script. Just don’t set them and let PowerShell throw an error if the parameter is not provided.

1 Like

Thank you all for your help. Here is what I eventually came up with to get this to work the way we wanted it to.

[CmdLetBinding()]
param(
	[Parameter(Mandatory=$True, HelpMessage="Path to SQL Query file")]
    [ValidateNotNullOrEmpty()]
    [string]$SQLQueryFile,
	[Parameter(Mandatory=$True, HelpMessage="Name of SQL Server")]
    [ValidateNotNullOrEmpty()]
    [string]$SQLServer,
    [Parameter(Mandatory=$False, HelpMessage="Name of SQL Instance on the server")]
    [string]$SQLInstance,
    [Parameter(Mandatory=$True, HelpMessage="Path to place the output file")]
    [ValidateNotNullOrEmpty()]
    [string]$ColumnName,
    [Parameter(Mandatory=$True, HelpMessage="Path to place the output file")]
    [ValidateNotNullOrEmpty()]
    [string]$OutputFilePath
)

# Set the file that has the SQL Query in it
if ($SQLQueryFile)
{
    try
    {
        Test-Path $SQLQueryFile -ErrorAction Stop | Out-Null
    }
    catch
    {
        $ErrorMessage = "[ERROR] Missing SQL Query File file -SQLQueryFile $SQLQueryFile"
        Write-Host "$ErrorMessage"
        exit
    }
    
    # Make sure you can import the query from the file.
    try
    {
        $SQLQuery = $SQLQueryFile
    }
    catch
    {
        $ErrorMessage = "[ERROR] Error importing SQL Query from file: $($SQLQueryFile)"
        exit
    }
}

#Set the SQL server name.
if ($SQLServer){
    $Server = $SQLServer
}

#Set the path to the output file you want.
if ($OutputFilePath){
}

#Set the SQL instance of the SQL server if there is multiple instances on the SQL server.
if ($SQLInstance){
    $Server = "$SQLServer\$SQLInstance"
}

#Create function for logging timestamps.
function Get-TimeStamp {
    
    return "[{0:MM/dd/yy} {0:HH:mm:ss}]" -f (Get-Date)
    
}

#Set up the folders and files needed for logging the script.
$LogFolder = "C:\Scripts\Results\Database"
$Exists = Test-Path -Path $LogFolder
if ($Exists -eq "True") {
    Write-Host "Directory Exists!"
} else {
    New-Item -Path $LogFolder -ItemType directory
}

$logdate = (Get-Date -UFormat ".%a %m-%d-%Y %H-%M %p")
$logfile = new-item -path $LogFolder -Name "Query-Database Results$logdate.log"

start-Transcript -path $Logfile

#Run the commands to perform the SQL query.
Invoke-Sqlcmd -InputFile "$SQLQuery" -ServerInstance "$Server" | Select-Object -ExpandProperty $ColumnName | Out-File "$OutputFilePath"

Stop-Transcript
2 Likes

Great job! Thanks for taking the time to come back and post your solution. I’m certain future visitors will benefit!

Thank you for the kind words and your help. I always felt that posting the actual solutions are the best thing to do in order to help those in the same situation.

1 Like