Within PowerShell, I want to run the Invoke-DbaQuery dbatools command and use a UNC path with the -File parameter:
Invoke-DbaQuery -SQLInstance $SQL_host -Database $DatabaseName -File "\\file_svr\scriptdir\userlist.sql"
The command runs without error but doesn't produce any output. Does anyone know if this is possible? If so, what am I missing?
It turns out that the call has been working but the output was not visible. This is because the script has code like this:
DECLARE UserCursor CURSOR FOR
SELECT
'CREATE USER ' + SPACE(1) + QUOTENAME([name]) + ' FOR LOGIN ' + QUOTENAME([name]) + ' WITH DEFAULT_SCHEMA = ' + QUOTENAME([default_schema_name]) + ';'
AS [-- SQL STATEMENTS --]
FROM sys.database_principals AS rm
WHERE [type] IN ('U', 'S', 'G') -- windows users, sql users, windows groups
AND [name] <> 'dbo'
AND [name] <> 'guest'
AND default_schema_name is not null
OPEN UserCursor
FETCH NEXT FROM UserCursor INTO #addUserScript
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #addUserScript
FETCH NEXT FROM UserCursor INTO #addUserScript
END
CLOSE UserCursor
DEALLOCATE UserCursor;
The solution then was to call the script like this:
Invoke-DbaQuery `
-SQLInstance $HCM_SQL_host `
-Database $Refresh_database `
-Verbose 4>&1 `
-File "\\file-srv0\scriptdir\Userlist.sql" | `
out-string -Width 250 | Out-File -FilePath $Create_Users_File_Fullname
Note the line "-Verbose 4?&1 `" that redirects the verbose output (i.e. the PRINT output) back to the standard output and, ultimately, the file.
Related
I'm trying to write a script which uses the powershell cmdlet get-content tail and inserts the new lines into the sql server table. i can't get the syntax to pipe the tail to the sqlinsert.ps1 file that handles the table insert.
i'm looking for help on how to pipe "get-content tail" to a sqlinsert.ps1 file to do a sql database insert statement using the following :
$startTime = get-date
Write-Host "\\iisserver\logs\Logs-$("{0:yyyyMMdd}" -f (get-date)).txt"
get-content "\\iisserver\logs\Logs-$("{0:yyyyMMdd}" -f (get-date)).txt" -tail 1 -wait | & "sqlinsert.ps1" -stmp $("{0:yyyy-MM-dd hh:mm:ss.fff}" -f (get-date)) -method "Error" -msg $_
# % { "$_ read at $(Get-Date -Format "hh:mm:ss")" }
in the sqlinsert.ps1 :
param ([string]$stmp, [string]$method, [string]$msg )
$Connection = New-Object System.Data.SQLClient.SQLConnection
$Connection.ConnectionString = "server='$serverName';database='$databaseName';User ID = $uid; Password = $pwd;"
$Command = New-Object System.Data.SQLClient.SQLCommand
$Command.Connection = $Connection
$sql = "insert into [tbl_iiserrors] (errstamp, method, msg) values (#stmp , #method, #msg) "
.
.
.
error i get:
& : The term 'sqlinsert.ps1' is not recognized as the name of a
cmdlet, function, script file, or operable program. Check the spelling
of the name, or if a path was included, verify that the path is
correct and try again. At C:\Temp\ob\iislog\tst_tail.ps1:3 char:95
... Mdd}" -f (get-date)).txt" -tail 1 -wait | & "sqlinsert.ps1" -stmp $ ...
~~~~~~~~~~~~~~~
CategoryInfo : ObjectNotFound: (sqlinsert.ps1:String) [], CommandNotFoundException
FullyQualifiedErrorId : CommandNotFoundException
Suggestion [3,General]: The command sqlinsert.ps1 was not found, but
does exist in the current location. Windows PowerShell does not load
commands from the current location by default. If you trust this
command, instead type: ".\sqlinsert.ps1". See "get-help
about_Command_Precedence" for more details.
The sqlinsert.ps1 works when i run it from powershell command :
PS c:\temp> .\sqlinsert -stmp 2020-11-20 00:00:00 -method 'eek' -msg 'uh hello'
In order to bind pipeline input to a parameter, you need to decorate it with a [Parameter] attribute and specify that it accepts pipeline input, like this:
param (
[string]$stmp,
[string]$method,
[Parameter(ValueFromPipeline = $true)]
[string]$msg
)
See the about_Functions_Advanced_Parameters help file for more details about how to modify the behavior of parameters
By design, for security reasons, PowerShell requires you to signal the intent to execute a script located in the current directory explicitly, using a path - .\sqlinsert.ps1 - rather than a mere file name - sqlinsert.ps1; that is what the suggestion following the error message is trying to tell you.
Note that you only need &, the call operator, if the script path is quoted and/or contains variable references - and .\sqlinsert.ps1 doesn't require quoting.
You can only use the automatic $_ variable, which represents the current input object from the pipeline inside a script block ({ ... }), such as one passed to the ForEach-Object cmdlet, which invokes that block for each object received via the pipeline.
Re the content of your script: Inside expandable strings ("..."), you cannot use # to refer to variables to be expanded (interpolated); use regular, $-prefixed variable references or $(...), the subexpression operator to embed expressions; also, it looks like you're inserting string values into the SQL table, so you'll have to enclose the expanded variable values in embedded '...'
$startTime = get-date
Get-Content "\\iisserver\logs\Logs-$("{0:yyyyMMdd}" -f (get-date)).txt" -Tail 1 -Wait |
ForEach-Object {
.\sqlinsert.ps1 -stmp ("{0:yyyy-MM-dd hh:mm:ss.fff}" -f (get-date)) -method "Error" -msg $_
}
The alternative to using a ForEach-Object call is to modify your script to directly receive its -msg argument from the pipeline, as shown in Mathias' answer, in which case you must omit the -msg $_ argument from your script call:
Get-Content ... |
.\sqlinsert.ps1 -stmp ("{0:yyyy-MM-dd hh:mm:ss.fff}" -f (get-date)) -method "Error"
I am running a script which triggers a query to fetch the worst performing SQL query for an interval of 1hr from the SQL server & also formatting the output using "Format-Table -AutoSize". But the query is not printing full query rather it is printing partial.
Code Snippet:
function PerformerQuery {
$i=1
$number=2
do{
$serverInstanceP = "SQLExpress2014"
$perfQuery="
USE [master]
SELECT TOP 20
total_worker_time/execution_count AS Avg_CPU_Time
,Execution_count
,total_elapsed_time/execution_count as AVG_Run_Time
,total_elapsed_time
,(SELECT SUBSTRING(text,statement_start_offset/2+1,statement_end_offset) FROM sys.dm_exec_sql_text(sql_handle)) AS Query_Text
FROM sys.dm_exec_query_stats
ORDER BY Avg_CPU_Time DESC
"
Invoke-Sqlcmd -ServerInstance $serverInstanceP -Database master -Query $perfQuery -QueryTimeout 9400| Format-Table -AutoSize
Start-Sleep -s 3
}
while ($i -le $number)
}
PerformerQuery
Tried with "Out-String" & "Out-Gridview", but the same output. Cannot use "ft" as it is printing the discrete data.
Please refer the screenshot as the output (added both command line output and database server output).
Database Server output:
Commandline output:
Did you try to add -Wrap to the Format-Table and/or pipe it to Out-String -Width 500 so it does not use the terminal width as a limit.
I'm trying to redirect the script output to a txt but it fails
Clear-Host
$Elementos =Get-WmiObject Win32_Battery -namespace 'root\CIMV2'
foreach ($Elemento in $Elementos) {
$Elemento.BatteryStatus
$Elemento.EstimatedChargeRemaining
$Elemento.EstimatedRunTime} >C:\temp\Prueba.txt
the result of the script is correct
2
100
71582788
And the resulting error is:
"The term '>' is not recognized as the name of a cmdlet, function,
script file or executable program. Check if you typed the name
correctly, or if a path included, verify that the path is correct and
try again. Published 7 Character: 2
+> <<<< C: \ temp \ Test.txt
+ CategoryInfo: ObjectNotFound: (>: String) [], CommandNotFoundException
+ FullyQualifiedErrorId: CommandNotFoundException"
I can't need to say that the path is correct.
If I run for instance:
PowerShell (Get-WmiObject win32_battery).estimatedChargeRemaining > C:\temp\Prueba.txt
that run's OK
Any idea what I'm doing wrong?
Thanks in advance.
Kind Regards.
Emilio Sancha
MS Access MVP 2006-2011
You can not pipe output of a ForEach loop. You can capture it in a variable, or pipe things inside the loop, but you cannot pipe the output of the entire loop in general. There's a couple things you could try...
Capture all output from the loop in a variable, and then output that variable to a file:
Clear-Host
$Elementos =Get-WmiObject Win32_Battery -namespace 'root\CIMV2'
$Output = foreach ($Elemento in $Elementos) {
$Elemento.BatteryStatus
$Elemento.EstimatedChargeRemaining
$Elemento.EstimatedRunTime
}
$Output>C:\temp\Prueba.txt
Or you could output inside the loop:
Clear-Host
$Elementos =Get-WmiObject Win32_Battery -namespace 'root\CIMV2'
foreach ($Elemento in $Elementos) {
$Elemento.BatteryStatus>>C:\temp\Prueba.txt
$Elemento.EstimatedChargeRemaining>>C:\temp\Prueba.txt
$Elemento.EstimatedRunTime>>C:\temp\Prueba.txt
}
Or in your case you could just use a Select command and output that to a file
Clear-Host
$Elementos =Get-WmiObject Win32_Battery -namespace 'root\CIMV2'
$Elementos | Select BatteryStatus,EstimatedChargeRemaining,EstimatedRunTime | Export-CSV C:\Temp\Prueba.txt -notype
Use Out-File instead of the caret.
https://technet.microsoft.com/en-us/library/ee176924.aspx
I'm having issues making calls to the local database using the method outlined below.
Error Message
invoke-sqlcmd : Value cannot be null.
Parameter name: ServerInstance
At C:\filelocation\HealthCheckCombined.ps1:86 char:3
1. invoke-sqlcmd -query $agentquery -serverinstance $servername ...
2. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Invoke-Sqlcmd], ArgumentNullException
+ FullyQualifiedErrorId : CannotGetServerInstance,Microsoft.SqlServer.Management.PowerShell.GetScriptCommand
Environment(s)
Server 2016 Candidate 3
Server 2012
SQL Server 2014
SQL Server 2012
PowerShell 3.0, 4.0 & 5.0
Goal
I'm trying to run a query via PowerShell against whatever SQL instance is listed in the servers.txt (config file).
Two components-
External Configuration File (servers.txt)
PowerShell script containing functions, loop to create an array from servers.txt and execute the function.
So the contents of servers.txt looks like=
server=test2k16\powershell
server=test2k16\healthcheck
Here's the section where I import the text file and create the function=
#===============================================================================
#Configurable variables
#===============================================================================
$configfile = 'C:\filelocation\servers.txt'
Import-Module "sqlps"
#===============================================================================
#===============================================================================
#SQL Agent Jobs
#===============================================================================
function SQLAgent{
$agentquery= #"
declare #count int
select #count = count(1) from msdb.dbo.sysjobs as sj
join msdb.dbo.sysjobhistory as sjh on sj.job_id = sjh.job_id
where sj.enabled != 0
and sjh.sql_message_id > 0
and sjh.run_date > CONVERT(char(8), (select dateadd (day,(-30), getdate())), 112)
and sjh.Step_id <= 1
if (#count >= 1)
begin
select distinct sj.name as SQLJobName
from msdb.dbo.sysjobs as sj
join msdb.dbo.sysjobhistory as sjh on sj.job_id = sjh.job_id
where sj.enabled != 0
and sjh.sql_message_id > 0
and sjh.run_date > CONVERT(char(8), (select dateadd (day,(-30), getdate())), 112)
and sjh.Step_id <= 1
order by name
end
else
begin
Select 'No Job Failed in Last Month' as SQLJobName
end
"#
invoke-sqlcmd -query $agentquery -serverinstance $servername -username "user" -password "password" | Format-Table -AutoSize -Wrap
}
#===============================================================================
Now I make the magic happen by formatting the imported variables and looping through them while running the function=
#===============================================================================
#Run Health Check for each server
#===============================================================================
$import = $(foreach ($line in get-content $configfile) {$line.tolower().split(" ")}) | sort | get-unique
ForEach ($_ in $import){
$servername = $import.trimstart("server=")
}
ForEach ($_ in $servername){
SQLAgent
}
#===============================================================================
Findings thus far
Extracting the code within in the function and importing the text file works perfectly fine. No error.
The $servername variable in the loop displays the correct values (test2k16\powershell & test2k16\healthcheck) if I change the script to only display those variables in the loop
I'm obviously missing something... I've been searching the stack and Google for a day now and finding nothing. Hopefully it's something small I overlooked or don't understand about PowerShell yet.
Thanks in advance for any help!
You are referencing $ServerName for the -ServerInstance parameter. It expects a string, and you are presenting it with an array of strings. Also, you are using the ForEach loop incorrectly the last two times. It should be a variable name, and not the automatic variable of $_. Example:
ForEach($Server in $ServerName){
SQLAgent
}
Then change your -ServerInstance in your function to reference $Server. Better yet, set parameters for your function and feed it the info it needs within your loop.
Function SQLAgent($Server){
<Code goes here!>
invoke-sqlcmd -query $agentquery -serverinstance $server -username "user" -password "password" | Format-Table -AutoSize -Wrap
}
$ServerList = Get-Content $configfile | ForEach{$_.Split('=')[1]}
ForEach($Item in $ServerList){
SQLAgent -Server $Item
}
I don't know where my error is in my powershell script.
#Add-PSSnapin SqlServerCmdletSnapin100
#Add-PSSnapin SqlServerProviderSnapin100
$DATA=IMPORT-CSV "C:\Users\pzd74f\Desktop\SLAPERFMAT2011.csv" -header("SLA1 Applications Availability")"
FOREACH ($LINE in $DATA)
{
$SLADefinition="`'"+$SLADefinition+"`'"
# insert into this
$SQLHEADER=”INSERT INTO [SP2010_EDCSLA_AppDBHIM].[dbo].SLAPerfMatrix ([SLADefinition])"
#insert into this
$SQLVALUES=" VALUES ($SLADefinition)"
$SQLQUERY=$SQLHEADER+$SQLVALUES
Invoke-Sqlcmd –Query $SQLQuery -ServerInstance localhost
}
The line
$DATA=IMPORT-CSV "C:\Users\pzd74f\Desktop\SLAPERFMAT2011.csv" -header("SLA1 Applications Availability")"
is syntactically incorrect
Remove extra " at the end.