Powershell, Invoke-SqlCmd and flushing output - powershell

How to force Invoke-SqlCmd in Powershell to flush its output? Basically the same question asked at https://github.com/PowerShell/PowerShell/issues/3060, where Powershell people say it's SQL's problem whereas "SQL folks say this is an issue with the default powershell output, not their code".
Here is a simple demo:
Write-Host "Run 1"
Invoke-Sqlcmd -Query "SELECT GETDATE() AS CurrentTime;"
#Read-Host -Prompt "press a key..."
Write-Host "Run 2"
Invoke-Sqlcmd -Query "SELECT GETDATE() AS CurrentTime;"
the output would look like this:
Run 1
Run 2
CurrentTime
-----------
8/23/2019 9:24:44 AM
8/23/2019 9:24:44 AM
whereas I am expecting that the first output is after "Run 1" and before "Run 2". If commenting out the Read-Host, the Invoke-Sqlcmd get separated but still not what I'm expecting:
Run 1
press a key...:
CurrentTime
-----------
8/23/2019 9:22:28 AM
Run 2
8/23/2019 9:22:32 AM
So, how to force Invoke-SqlCmd in Powershell to flush its output?

Please see your script after modification below:
$run1 = Invoke-Sqlcmd -Query "SELECT GETDATE() AS CurrentTime;" | Out-String;
Write-Host "Run 1 is $run1"
#Read-Host -Prompt "press a key..."
Start-Sleep 2 #To simulate different timing
$run2 = Invoke-Sqlcmd -Query "SELECT GETDATE() AS CurrentTime;" | Out-String
Write-Host "Run 2 is $run2"
And the output will be,
Run 1 is
CurrentTime
-----------
8/23/2019 11:51:47 AM
Run 2 is
CurrentTime
-----------
8/23/2019 11:51:49 AM

Related

Powershell Date

I have a personalized powershell profile, on my $PROFILE I have added the Get-Date command and it gives me the date at the moment the script is executed, I wanted to know if it is possible to have the time updated every second or five and still being able to use the powershell, Ive tried a lot of possibilities but cant find one that works, if thi is not possible is there anything similar?
thanks.
ive tried a lot of whiles, jobs ...
here some examples of what I have tried:
$script = {
while ($true) {
Write-Output (Get-Date -Format G)
Start-Sleep -Seconds 1
}
}
Start-Job -ScriptBlock $script
while ($true) {
Write-Host (Get-Date -Format G) -NoNewline
Start-Sleep -Seconds 1
Write-Host "`r" -NoNewline
}
You could modifiy the prompt function for this. This way it won't interfere with commands output and the terminal.
Check current definition with:
(Get-Command prompt).ScriptBlock
It should be something like this:
"PS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) ";
Then modify it for current session only (just paste and execute in terminal):
function prompt {
"$(get-date -f 'G') PS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) ";
}
prompt before:
PS C:\Users\username>
prompt after:
2023-02-09 16:13:13 PS C:\Users\username>
If it works as expected, you can place new function definition in $PROFILE. This way you'll always have the time of all meaningful events, i.e. every command/script start and completion.

Store the output of command ran on remote system and list in proper format and select the option from that list in powershell?

I am running the below command in one of my powershell function to get the list of services from remote computer
Get-WmiObject -Class Win32_Service -filter "name like '$envlist%'" -Impersonation 3 -Credential abcdomain\PXXXX -ComputerName $server | Format-List -Property Name
The above command gives me the below output and I want to store it in variable and list it in proper format
Name : ABC_xx02_TT_xcedf_1.0.00.0101
Name : ABC_xx02_TT_nghk_2.1.0.99999
Name : ABC_xx02_TT_nmk_3_1.0.3.7890
Name : ABC_xx02_TT_pnp_4.0.0.123
I am expecting the output in below way:(the below services could be more so need to use something like counter).Once I select any choice from below I want to store it in variable for e.g if I select "3" then it should store in variable $serivcename = ABC_LA02_TT_nmk_3_1.0.3.7890
1. Press 1 to select ABC_xx02_TT_xcedf_1.0.00.0101
2. Press 2 select ABC_xx02_TT_nghk_2.1.0.99999
3. Press 3 to select ABC_xx02_TT_nmk_3_1.0.3.7890
4. Press 4 to select ABC_xx02_TT_pnp_4.0.0.123
Please make a selection:
I have no idea of course what could be in '$envlist%', but apparently that gives you the result you need.
Right now, your code uses Format-List, but that does not display a usable console menu.
I would do it like this:
$collection = (Get-WmiObject -Class Win32_Service -Filter "name like '$envlist%'" -Impersonation 3 -Credential abcdomain\PXXXX -ComputerName $server).Name
while ($true) {
Clear-Host
for ($i = 1; $i -le $collection.Count; $i++) {
# build your menu
"{0}. Press {0} to select {1}" -f $i, $collection[$i - 1]
}
$answer = Read-Host "Please make a selection. Press Q to quit"
# test if the (string) answer is in range, or if the user wants to quit
# if so, break the while loop
if ($answer -match "[1-$($collection.Count)Q]") { break }
# when you reach this point, the user made an invalid choice
Write-Host "Invalid input, please try again" -ForegroundColor Red
Start-Sleep -Seconds 3
}
# do whatever you need with the selected option
if ($answer -ne 'Q') {
Write-Host "User selected '{0}'" -f $collection[[int]$answer - 1]
# DoStuff $collection[[int]$answer - 1]
}
Try this:
$i = 0
Get-WmiObject -Class Win32_Service -filter "name like '$envlist%'" -Impersonation 3 -Credential abcdomain\PXXXX -ComputerName $server | Foreach {
$i++
Write-host "$($i). Press $($i) to select $($_.name.split('=')[1])"
}
$input = read-host "Please make a selection"
Now selection will be stored in $input.

"Query_Text" seems incomplete while running from powershell using SQL cmd

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.

Powershell invoke-sqlcmd : Value cannot be null. Parameter name: ServerInstance

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
}

Powershell - Loop script until user chooses to exit

How can I start a script over again? I have 3 switches and I want them to revert back to the beginning of the script.
Import-Module ActiveDirectory
Write-Host "--Please Login using a.account--"
#login
$credential = Get-Credential
#Main
Write-Host "--Remote Computer Rename v2.0--"
Write-Host "1. Query AD (Outputs to a text file)"
Write-Host "2. Quick computer rename"
Write-host "3. Quit"
$choice=Read-Host "Chose a number to continue"
#AD Query for computer
switch ($choice)
{
1 {
Write-Host "--Enter first five characters of computer name or full computer name i.e. USCLT--"
$cn=Read-Host 'Computer name'
$out="$cn*"
Get-ADComputer -Filter 'SamAccountName -like $out' >> c:\myscripts\dsquery.txt
Write-Host "Query complete. See dsquery.txt saved to Desktop."
}
...rest of my code.
So after See dsquery.txt saved to Desktop." I want it to go back to write-host portion.
Simple, short, stupid:
& cmd /c pause
exit
This will even contribute the "Press any key" message the TO requested. If you prefer to stay in PowerShell:
Read-Host "Press any key to exit..."
exit
But we may also get the input back:
$reply = Read-Host "Please type EXIT to exit"
if ($reply -eq "EXIT") { exit; }
I like that Read-Host exits the script when typing Ctrl-C, like cmd's pause does.
My personal favorite for checking user input is the do { } until () loop. Here is your code with the added loop, this will accomplish what your looking for:
Import-Module ActiveDirectory
Write-Host "--Please Login using a.account--"
#login
$credential = Get-Credential
#Main
do {
Write-Host "--Remote Computer Rename v2.0--"
Write-Host "1. Query AD (Outputs to a text file)"
Write-Host "2. Quick computer rename"
Write-host "3. Quit"
$choice=Read-Host "Chose a number to continue"
#AD Query for computer
switch ($choice)
{
1 {
Write-Host "--Enter first five characters of computer name or full computer name i.e. USCLT--"
$cn=Read-Host 'Computer name'
$out="$cn*"
Get-ADComputer -Filter 'SamAccountName -like $out' >> c:\myscripts\dsquery.txt
Write-Host "Query complete. See dsquery.txt saved to Desktop."
}
...rest of my code.
} until ($choice -eq 3)
This is a pretty unique strategy in my opinion. I took this from Jerry Lee Ford’s book : Microsoft Windows PowerShell Programming for the absolute beginner
you can read more about these and every other loop in powershell here : http://www.powershellpro.com/powershell-tutorial-introduction/logic-using-loops/
From a blog post I found:
echo "Press any key to exit..."
$Host.UI.RawUI.ReadKey("NoEcho, IncludeKeyDown") | OUT-NULL
$Host.UI.RawUI.FlushInputbuffer()
Enclose the whole thing in a while (1) {} block. That creates an infinite loop that will only terminate if it encounters a break (end the loop) or exit (end the script). Presumably, option 3 will lead to one of those statements.