Concat invoke-SqlCmd query string does not work - powershell

How can I concat a list of parameter OR a string of parameter (better) to my sql query? The below does not work.
$parameters = #("-ServerInstance `"MyMachine\SQLEXPRESS`"", "-Database %TargetDbName%", "-Username %SQLUserName%", "-Password %SQLPassword%")
$row = Invoke-Sqlcmd -Query "SELECT field FROM Table;" $parameters
I want to execute later multiple queries all with the same connection parameters and it is usefull to reuse them in a string which I can just add to the query string.

You were on the right track. Sounds like you are looking for splatting.
Splatting is a method of passing a collection of parameter
values to a command as unit.
I don't use Invoke-SQLcmd but it should work just like this:
$parameters = #{
ServerInstance = "MyMachine\SQLEXPRESS"
Database = "TargetDbName"
Username = "SQLUserName"
Password = "SQLPassword"
Query = "SELECT field FROM Table;"
}
$row = Invoke-Sqlcmd #parameters
Collect all the parameters as a hashtable and splat the cmdlet. If you wanted to use this parameter set again later, but make small changes, that would be easy now by referencing the name/value pair of the hashtable.
$parameters.Query = "SELECT field FROM DifferentTable;"
$anotherRow = Invoke-Sqlcmd #parameters

Have a look at parameter splatting
This means that you can put the arguments into a hashtable and pass the hashtable as parameters. Given your code, you could change it to. Notice that even though i assign the parameters to a hashtable $parameters you have to send it to the cmdlet using the #parameter syntax.
$parameters = #{ServerInstance="MyMachine\SQLEXPRESS";Database="$env:TargetDbName";Username="$env:SQLUserName";Password="$env:SQLPassword"}
$row = Invoke-Sqlcmd -Query "SELECT field FROM Table;" #parameters
I assumed that the TargetDBName, Username and password were to be found in environment variables so i changed the code a little to get those as well.
Give it a go.

Related

how to use both parameters and hard-code the servers list through powershell

I am trying to create a script which should run using parameter and without parameter should result for for multiple list:
Ex: I have a function under which I gave param as servername which is working fine for single server ad fetching the results.
(RestoreFileList -servername XXXXXXXXXXX1)
If I do not want to give a parameter as servername and output should result full set of servers list data
Ex : Restorefilecount
Am i missing something between parameter and serverlist condition which should fetch the results, Any help on this?
Script *************
function Restorefilecount()
{
[cmdletbinding()]
param(
[Parameter(position = 0,mandatory = $false)]
[string] $Servername
)
$servername = #()
$servername = ('XXXXXXXX1', 'XXXXXXXXX2', 'XXXXXXXXX3', 'XXXXXXXXXX4')
$result = Invoke-Client -ComputerName $Servername -ScriptBlock {
$Server = HOSTNAME.EXE
$Query = #'
Select ##ServerName AS ServerName , name,(SUBSTRING(NAME ,15,3) * 100 ) / 100 AS
file,state_desc,user_access_desc FROM master.sys.databases where name like 'TKT' ORDER BY 2
'#
Invoke-Sqlcmd -ServerInstance $Server -Database master -Query $Query
}
Building on Abraham Zinala's helpful comments:
It looks like you're simply looking to define a parameter's default value:
function Restore-FileCount {
[CmdletBinding()]
param(
[string[]] $Servername = #('XXXXXXXX1', 'XXXXXXXXX2', 'XXXXXXXXX3', 'XXXXXXXXXX4')
)
# ...
$Servername # Output for demo purposes
}
Note how the parameter type had to be changed from [string] to [string[]] in order to support an array of values.
Incidental changes:
Since you're using a param(...) block to define your parameters (which is generally preferable), there is no need to place () after the function name (Restorefilecount()) - while doing so doesn't cause a syntax error as long as there's nothing or only whitespace between ( and ), note that you declare parameters either via a param(...) block or via function foo(...); also, in parameter-less functions () is never needed - see the conceptual about_Functions help topic.
I've inserted a hyphen (-) in your function name, to make it conform to PowerShell's naming convention.
I've omitted [Parameter(position = 0, mandatory = $false)], because what this attribute instance specifies amounts to the default behavior (all parameters are non-mandatory by default, and Position=0 is implied by $ServerName being the first (and only) parameter).

it is possible to pass argument to powershell script a datatable?

I'm trying to create a PowerShell script that inserts a datatable to SQL via WriteToServer...
This script is called by a PowerAutomateDesktop automation.
So... I cannot pass my datatable as an argument :(
%dt% it s datatable variable which needs to be used inside powershell script.
This is my dilemma - it is interpreted as a string or something like that
#Invoke-sqlcmd Connection string parameters
$params = #{'server'='SQLEXPRESS';'Database'='Db'}
Write-Output %dt%
#Variable to hold output as data-table
$dataTable = %dt% | Out-DataTable
#Define Connection string
$connectionString = "Data Source=DSQLEXPRESS; Integrated Security=SSPI;Initial Catalog=Db"
#Bulk copy object instantiation
$bulkCopy = new-object ("Data.SqlClient.SqlBulkCopy") $connectionString
#Define the destination table
$bulkCopy.DestinationTableName = "dbo.__SALES"
#load the data into the target
$bulkCopy.WriteToServer($dataTable)
#Query the target table to see for output
Invoke-Sqlcmd #params -Query "SELECT * FROM dbo.__SALES" | format-table -AutoSize
Thanks!
UPDATE
No loner need to pass an argument - I create the datatable inside the script.
Thanks again!
Work-around: create the datatable inside the script

Passing multiple variables from csv file to invoke-sqlcmd

I'm trying to read values from a CSV file, embed them into a INSERT T-SQL statement and run that statement using Invoke-Sqlcmd.
Here's my code:
Push-Location; Import-Module SQLPS -DisableNameChecking; Pop-Location
$InsertQry = "insert into $ImportTable VALUES ('`$(Col1)','`$(Col2)','`$(Col3)','`$(Col4)') "
Import-CSV $ImportFile | ForEach-Object { `
$RowData = "Col1=$($_.{My Ref})","Col2=$($_.{General satisfaction})","Col3=$($_.Helpfulness)","Col4=$($_.Effort)"
Invoke-Sqlcmd `
-Database $DBName -ServerInstance $SQLServer `
-Query $InsertQry `
-Variable $RowData
}
The script works fine for rows in the CSV file that contain values for each column. Unfortunately for me, some of the rows in the CSV file contain empty values (so perhaps only the first two columns contain values). These rows fail to be inserted into the table, and generate the following error:
Invoke-Sqlcmd : The format used to define the new variable for
Invoke-Sqlcmd cmdlet is invalid. Please use the 'var=value' format for
defining a new variable.
The potentially empty columns are all columns that are either empty or contain a single digit number 1 to 5.
I've tried various ways to escape the value, cast it to a different data type, add zero or an empty string to it, null coalesce it, but I cannot get a solution that works.
I have control over the destination table, so I'd be happy to pass zero, empty string, null or any other value as a placeholder for the empty values.
As per the documentation you are to pass variables in a string array where each element has a "key=value" format. You are building that correctly. Invoke-SQLCMD seems to take offence to null values being passed. The nulls of course are coming from blank entries in your CSV. Assuming you allow nulls in those columns then perhaps you could just adjust the query as each loop pass instead.
Push-Location; Import-Module SQLPS -DisableNameChecking; Pop-Location
$InsertQry = "insert into $ImportTable VALUES ('{0}','{1}','{2}','{3}')"
$propertiesToSplat = #{
Database = $DBName
ServerInstance = $SQLServer
}
Import-CSV $ImportFile | ForEach-Object {
$propertiesToSplat.Query = $InsertQry -f $_."My Ref", $_."General satisfaction", $_.Helpfulness, $_.Effort
Invoke-Sqlcmd #propertiesToSplat
}
So at each loop pass we use the format operator to insert the column values into your insert statement. Using curly braces in property names is useful when your properties contain special characters. Since you just have to deal with a space; quotes work just as well.
I also wanted to show you splatting which is a method to pass properties as a hashtable to a cmdlet. This lets you edit props on the fly and keep your lines shorter without having to worry about backticks everywhere.
Edit - completely new answer
I suck at ForEach-Object. This a foreach loop that checks the value of "General staisfaction" for each line in the CSV, and replaces it with a placeholder string before completing the $RowData variable. Unfortunately I cannot test it here; please let me know how you get on.
Push-Location; Import-Module SQLPS -DisableNameChecking; Pop-Location
$InsertQry = "insert into $ImportTable VALUES ('`$(Col1)','`$(Col2)','`$(Col3)','`$(Col4)') "
$myCSVFile = Import-CSV $ImportFile
foreach($line in $myCSVFile){
if($line."General staisfaction" -eq $null -or $line."General staisfaction" -eq ""){
$line."General staisfaction" = "placeholder"
}
$RowData = "Col1=$($line.{My Ref})","Col2=$($line.{General satisfaction})","Col3=$($line.Helpfulness)","Col4=$($line.Effort)"
Invoke-Sqlcmd -Database $DBName -ServerInstance $SQLServer -Query $InsertQry -Variable $RowData
}

Is it possible to use variable nested command splatting

I am just wanting to check if it is possible to use If statements to set the value of a nested command splat in PowerShell. Below is what I have currently:
$SADUParams = #{
Identity = $SAM
Company = $Company
Server = $ADserver
Replace = #{"extensionattribute11"="$Department";"extensionattribute12"="RESOURCE"}
}
if($PhoneNo){
$SADUParams["OfficePhone"] = $PhoneNo
}
Set-ADUser #SADUParams
What I would like to also do is update ExtensionAttribute12 using an if statement like with the phone number in the example above.
Is this possible, and if so, how do I code it?
Sure, just simple access the desired property from the $SADUParams hashtable and update it:
if ($true <#yourcondition#>)
{
$SADUParams.Replace.extensionattribute12 = "your_new_value"
}

Create Function Parameter Accept Wild Cards

I have a function that I am writing and I would like to create a wild card process on the WMI call. Wondering what my options might be. My first thought was to take the parameter value sent and replace any asterisk with percent signs and have a case statement that will use a query string depending on if I need to use a LIKE statement or not. Am I over complicating it and is there a much simpler way I have not found?
Here is the top portion of the function as it so
[CmdletBinding()]
Param(
[Parameter(Mandatory=$false,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$True)][string]$Name = "",
[Parameter(Mandatory=$false,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$True)][string]$ComputerName = $env:ComputerName,
[Parameter()][string]$Port = "",
[Parameter()][switch]$Full
)
Process {
if($ComputerName.Trim()) {
try {
$printers = (Get-WmiObject -Query "select * from Win32_printer where portname like '%$port%' and name like '%$Name%' " -ComputerName $ComputerName -EnableAllPrivileges -ErrorAction Stop)
This is kind of what I was trying to no avail.
We clear the clutter for this function to show the bare minimum needed to prove our point. Like's strong point is using wildcards however the comparison will still function without them. Only caveat is that the could be a performance issue and that = would be better. Point being is that, for simplicity sake at least, we are going to optionally add the wildcards to the query string.
Function Get-Printers{
[CmdletBinding()]
Param(
[Parameter()][string]$Name = "",
[Parameter()][string]$Port = "",
[Parameter()][switch]$Wild = $True
)
$wildcard = if($wild){"%"}Else{$null}
Get-WmiObject -Query ("select * from Win32_printer where portname like '{0}$port{0}' and name like '{0}$Name{0}'" -f $wildcard)
}
So by default the function will use wildcards. In the query string we use the format operator to add in whatever the $wildcard was determined to be. So if the -Wild was true then the query would look like this:
select * from Win32_printer where portname like '%Fax%' and name like '%Fax%'
Else, if it was false then they same query would look like this:
select * from Win32_printer where portname like 'Fax' and name like 'Fax'
To reiterate, with the latter query in mind, the use of like without wildcards will produce the same results as if we just had =.
With Matts reference I found that this seems to work as well. I was trying to get all crazy before with a case statement and checking the value passed via $name[0] and $name[-1] to see what position the wild card value was to define the particular case statement to run not thinking that the like statement doesn't really care.
Function Get-Printers{
[CmdletBinding()]
Param(
[Parameter()][string]$Name = "*",
[Parameter()][string]$Port = "*"
)
$Name = $Name.Replace('*','%')
$Port = $Port.Replace('*','%')
Get-WmiObject -Query ("select * from Win32_printer where portname like '$port' and name like '$Name'")
}
Thanks Matt!