Command is running fine in console but not when used by Powershell - sql-server

I have a PS script that should deploy a project to my SSIS server.
When I run the generated command in a console it runs fine but when the command is executed from Powershell it fails because of this (windows) error :
TITLE: SQL Server Integration Services
The path format is invalid.
Parameter name: DestinationPath (ISDeploymentWizard)
ADDITIONAL INFORMATION:
The path format is invalid. (Microsoft.SqlServer.IntegrationServices.Wizard.Common)
If I run the generated command from a console it runs fine:
D:\Deploy\ISDeploymentWizard.exe /Silent /ModelType:Project /SourcePath:"D:\Deploy\Receive\My_Beautiful_Project.ispac" /DestinationServer:"localhost" /DestinationPath:"/SSISDB/My Beautiful Project/My_Beautiful_Project" /ProjectPassword:"SuperSecretPassword"
The script (thanks to suggestions from Guenther Schmitz and Janne Tukaanen) :
#region script configuration
$SsisServer = "."
$ProjectFileFolder = "D:\Deploy\Receive"
$ProjectFileName = "My_Beautiful_Project.ispac"
$ProjectFilePassword = "SuperSecretPassword"
$FolderName = "My Beautiful Project"
$ProjectName = "My_Beautiful_Project"
$ISDeploymentWizard = "D:\Deploy\ISDeploymentWizard.exe"
#endregion
#region project deployment
# Create command line arguments
$DestinationPath = "/SSISDB/" + $FolderName + "/" + $ProjectName
$ProjectFilePath = $ProjectFileFolder + "\" + $ProjectFileName
$cmd = $ISDeploymentWizard
$arg1 = "/Silent"
$arg1a= "/ModelType:Project"
$arg2 = "/SourcePath:""$ProjectFilePath"""
$arg3 = "/DestinationServer:""$SsisServer"""
$arg4 = "/DestinationPath:""$DestinationPath"""
$arg5 = "/ProjectPassword:""$ProjectFilePassword"""
Write-Host "$cmd" $arg1 $arg1a $arg2 $arg3 $arg4 $arg5
& "$cmd" $arg1 $arg1a $arg2 $arg3 $arg4 $arg5
Write-Host "Done"
#endregion

There is no need to declare the following variables $arg1 $arg1a $arg2 $arg3 $arg4 $arg5, just run the following command (why declaring variables and storing their values in another variables??):
& $cmd /Silent /ModelType:Project /SourcePath:$ProjectFilePath /DestinationServer:$SsisServer /DestinationPath:$DestinationPath /ProjectPassword:$ProjectFilePassword

you are missing the executable in the line below Write-Host.
change
& $arg1 $arg2 $arg3 $arg4 $arg5
to
& $cmd $arg1 $arg2 $arg3 $arg4 $arg5

If you have troubles to start console apps in powershell (typically because of multiple arguments), you may execute it through cmd (in powershell)
cmd /c "$cmd $arg1 $arg2 $arg3 $arg4 $arg5"
There is also another option using Process class, so you don't have to use cmd:
$ProcessInfo = New-Object System.Diagnostics.ProcessStartInfo
$ProcessInfo.FileName = "D:\Deploy\ISDeploymentWizard.exe"
$ProcessInfo.Arguments = "$arg1 $arg1a $arg2 $arg3 $arg4 $arg5"
$ProcessInfo.RedirectStandardError = $true
$ProcessInfo.RedirectStandardOutput = $true
$ProcessInfo.UseShellExecute = $false
$Process = New-Object System.Diagnostics.Process
$Process.StartInfo = $ProcessInfo
$Process.Start() | Out-Null
$output = $Process.StandardOutput.ReadToEnd()
$errors = $Process.StandardError.ReadToEnd()
$Process.WaitForExit()
Write-Host $output
Write-Error $errors
You can check this for some more details:
PowerShell, stream Process output and errors while running external process

Pretty sure that the $DestinationPath needs to be a non relative path. Change it to the full path including the drive and I think that will solve your problem.

Related

Using PowerShell, how do I call CURL.exe using CMD.exe AND display results in WinForm

I am building a dashboard (WinForm) for internal usage, written in PS, and have a section where users can query sites using various methods/tools for inspection.
All the other methods/tools (IWR, nslookup via CMD call, OpenSSL via CMD call, CURL via CMD call, etc.) have no issues displaying their query results within a Form TextBox... and results for CURL commands seem to execute, return and save results properly in a PS string variable fine when run just within PS (see demo), but when running from the Form, PS seems to want console input for some reason and never displays the stored string in the Form (I presume because it isn't populating the same when called from the Form).
To minimally reproduce the issue, here is the code...
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Windows.Forms.Application]::EnableVisualStyles()
$Form = New-Object system.Windows.Forms.Form
$Form.Size = New-Object System.Drawing.Size(600,880)
$form.MaximizeBox = $false
$Form.StartPosition = "CenterScreen"
$Form.FormBorderStyle = 'Fixed3D'
$Form.Text = "Query Dashboard"
$Font = New-Object System.Drawing.Font("Arial",9,[System.Drawing.FontStyle]::Bold)
$form.Font = $Font
#####RESULTSBOX and OUTPUT BUTTONS####
$Results_txtbox = New-Object System.Windows.Forms.RichTextBox
$Results_txtbox.Multiline = $true
$Results_txtbox.ScrollBars = "Vertical"
$Results_txtbox.Location = New-Object System.Drawing.Size(10,490)
$Results_txtbox.Size = New-Object System.Drawing.Size(485,270)
$Form.Controls.Add($Results_txtbox)
##########CURL#############
#divider
$CURLLinediv = New-Object System.Windows.Forms.Label
$CURLLinediv.Text = ""
$CURLLinediv.BorderStyle = "Fixed3D"
$CURLLinediv.AutoSize = $false
$CURLLinediv.height = 2
$CURLLinediv.width = 550
$CURLLinediv.Location = New-Object System.Drawing.Size(20,340)
$Form.Controls.Add($CURLLinediv)
$LabelCURL = New-Object System.Windows.Forms.Label
$LabelCURL.Text = "CURL"
$LabelCURL.AutoSize = $true
$LabelCURL.Location = New-Object System.Drawing.Size(30,342)
$Form.Controls.Add($LabelCURL)
$CURL_query_txtbox = New-Object System.Windows.Forms.TextBox
$CURL_query_txtbox.Location = New-Object System.Drawing.Size(20,360)
$CURL_query_txtbox.Size = New-Object System.Drawing.Size(300,20)
$CURL_query_txtbox.Text = "-s www.google.ca"
$CURL_query_txtbox.add_MouseHover($CURLquery_Help)
$Form.Controls.Add($CURL_query_txtbox)
$CURL_query_tip = New-Object System.Windows.Forms.ToolTip
$CURLquery_Help ={ $tip = "Enter entire CURL CMD here starting after curl"
$CURL_query_tip.SetToolTip($this,$tip)
}
$CURL_button = New-Object System.Windows.Forms.Button
$CURL_button.Location = New-Object System.Drawing.Size(325,355)
$CURL_button.Size = New-Object System.Drawing.Size(40,22)
$CURL_button.Text = "GET"
$CURL_button.Add_Click({CURL})
$Form.Controls.Add($CURL_button)
function CURL
{
#MOCK
#$CURL_query_txtbox.Text = "-s www.google.ca"
###$Results_txtbox.Text = (curl.exe $($CURL_query_txtbox.Text) 2>&1 | % ToString) -join "`n"
###$Results_txtbox.Text = (curl.exe -s www.google.ca 2>&1 | % ToString) -join "`n"
$Results_txtbox.Text = Invoke-Expression "curl.exe $($CURL_query_txtbox.Text) 2>&1"
}
$result = $Form.ShowDialog()
If you hit the GET button, the results should show in the Results textbox, but instead of returning the result to the form, PS wants an IWR parameter (if you enter www.google.ca or another Uri, it will return back to the application but does nothing with the input, and doesn't update the textfield... but you can now close the Form.
Now if in your PS window you run
$Results_txtbox.Text = (curl.exe -s www.google.ca 2>&1 | % ToString) -join "`n"
you can see that it populates $Results_txtbox.Text correctly when running from PS, and if you mock (ie - $CURL_query_txtbox.Text = "-s www.google.ca" ) first, you will see that
$Results_txtbox.Text = Invoke-Expression "curl.exe $($CURL_query_txtbox.Text) 2>&1"
works perfectly in PS alone, but when called from the Form it doesn't return any results, and the same cmdlet Invoke-WebRequest... message shows up in the PS console looking for a parameter to be entered.
The error message suggests that you accidentally called the curl alias, which is Windows PowerShell's built-in alias for the Invoke-WebRequest cmdlet (note that PowerShell [Core] 7+ no longer has this alias).
Even though you meant to call your custom CURL function, the built-in alias took precedence, because aliases have higher precedence than functions - see about_Command_Precedence.
A simple solution is to give your function a different name.
Also, you don't need cmd.exe to make your curl.exe call:
$Results_txtbox.Text = (curl.exe -s www.google.ca 2>&1 | % ToString) -join "`n"
If the target command - curl.exe -s www.google.ca - comes from a text box filled by the user and you trust that input, use Invoke-Expression, though note that, in general, it should be avoided.
$Results_txtbox.Text = (
Invoke-Expression "$($CURL_query_txtbox.Text) 2>&1" | % ToString
) -join "`n"
If only the arguments come from the text box:
Invoke-Expression "curl.exe $($CURL_query_txtbox.Text) 2>&1" | % ToString
Note:
% ToString (short for: ForEach-Object { $_.ToString() }) is only needed in Windows PowerShell, to ensure that stderr lines - which PowerShell wraps in ErrorRecord instances - are represented as-is in a string context - this is no longer necessary in PowerShell [Core] 7+.
-join "`n" (or -join [Environment]::NewLine, if you want to use the platform appropriate newline [sequence], though that is usually not necessary) is used to join the array of output lines to form a single, multi-line string; while piping to Out-String works similarly, it - unfortunately - always adds a trailing newline.
This unfortunate aspect of Out-String's behavior is discussed in GitHub issue #14444.
Also note that the stream-merging redirection, 2>&1, is embedded in the string passed to Invoke-Expression.
One would expect the following form to work as well:
Invoke-Expression $CURL_query_txtbox.Text 2>&1
However, as of PowerShell 7.1 it does not; this unexpected behavior is discussed in GitHub issue #14503.

Parameterised powershell script through SSIS Execute Process Task (with UNC)

I've done varied amounts of research into how to run a parameterised PS script from SSIS. I am having issues getting a parameterised script running. PS script is as follows, if I hard code the parameters into the script it behaves as expected:
Param ([string]$filepath,[string]$filename)
$Path = $filepath
$InputFile = (Join-Path $Path $filename)
$Reader = New-Object System.IO.StreamReader($InputFile)
While (($Line = $Reader.ReadLine()) -ne $null) {
If ($Line -match 'FILE\|([^\|]+)') {
$OutputFile = "$($matches[1]).txt"
}
Add-Content (Join-Path $Path $OutputFile) $Line
}
Running in SSIS execute process task, I am trying to build the Arguments command via an expression as follows:
"-ExecutionPolicy ByPass -File " + #[User::vPSScriptLocation] + " " + #[User::vFilePath]+ " "+ #[User::vFileName]
Evaluating the expression gives the following:
-ExecutionPolicy ByPass -File \\WorkDirectory\Script.ps1 \\transfer datafile.data
Upon execution, the task fails. The .ps1 is deleted from the work directory and SSIS gives the following error code:
Error: 0xC0029151 at Execute powershell script, Execute Process Task: In Executing "C:\Windows\System32\WindowsPowerShell\v1.0\PowerShell.exe" "-ExecutionPolicy ByPass -File \\WorkDirectory\Script.ps1 \\transfer datafile.data" at "", The process exit code was "-196608" while the expected was "0".
Looks like it's getting an empty string where it shouldn't? Any pointers appreciated.
OK so as it appears I could not call a UNC path to execute this using an Execute Process Task, I decided to execute this within a Script Task with a reference added to System.Management.Automation which allowed me to create a PowerShell instance. This is far from my ideal solution as I really wanted to call a .ps1 file, but looks like this is my only solution given I need to use a UNC path.
I build the PS script with my Dts variables and then executed it within the instance, which achieved the desired result:
public void Main()
{
string filepath = Dts.Variables["User::vUNCPath"].Value.ToString();
string filename = Dts.Variables["User::vFileName"].Value.ToString();
string searchterm = Dts.Variables["User::vSearchTerm"].Value.ToString();
bool fireAgain = true;
// Build powershell script
string script = "$Path = \""+filepath+"\";"+
"$InputFile = (Join-Path $Path \""+ filename+"\");" +
"$Reader = New-Object System.IO.StreamReader($InputFile);" +
"While (($Line = $Reader.ReadLine()) -ne $null) {" +
"If ($Line -match '"+searchterm+"') { "+
"$OutputFile = \"$($matches[1]).txt\"};" +
"Add-Content (Join-Path $Path $OutputFile) $Line}";
Dts.Events.FireInformation(0, "Info", "Powershell script built: " + script, String.Empty, 0, ref fireAgain);
try
{
// Create instance to run script
using (PowerShell psinstance = PowerShell.Create())
{
//Assign built script to this instance
psinstance.AddScript(script);
//Run powershell script
psinstance.Invoke();
}
Dts.TaskResult = (int)ScriptResults.Success;
}
catch (Exception ex)
{
Dts.Events.FireError(0, "Error", ex.Message, String.Empty, 0);
Dts.TaskResult = (int)ScriptResults.Failure;
}
}
If you're running your script as a ps1-File with a param-block like this, your execution-call should name the parameters by their name:
"-ExecutionPolicy ByPass -File " + #[User::vPSScriptLocation] + " -filepath " + #[User::vFilePath]+ " -filename "+ #[User::vFileName]
This should do it, if you use a valid filepath and filename.
If it doesn't work, please try to write your script as a function and try it in a powershell-console. Your script as a function looks like this:
function SearchLines
{
Param (
[string]$filepath,
[string]$filename
)
$Path = $filepath
$InputFile = (Join-Path $Path $filename)
$Reader = New-Object System.IO.StreamReader($InputFile)
While (($Line = $Reader.ReadLine()) -ne $null) {
If ($Line -match 'FILE\|([^\|]+)') {
$OutputFile = "$($matches[1]).txt"
}
Add-Content (Join-Path $Path $OutputFile) $Line
}
}
Usage:
SearchLines -filepath "\\your\unc\path\here" -filename "filename.txt"
If this don't work for you, please let us know which error you got.
Thx.
UPDATE:
Based on your comments, i wrote your function new in the hope, it meets your requirements as close as possible. The function now looks like this:
function SearchLines
{
Param (
[string]$InputFile
)
$FileContent = Get-Content $InputFile
foreach($Line in $FileContent)
{
If ($Line -match 'FILE\|([^\|]+)')
{
$OutputFile = "$($matches[1]).txt"
}
Add-Content -Path $OutputFile -Value $Line
}
}
Usage:
SearchLines -InputFile c:\your\path\to\your\file.log
This function creates for every line in your given file a new file in the actual folder named what is written in the line. The Cmdlet Join-Path simply adds the two strings together, without any check for plausibility. That's why you can simply commit the full path to your file instead of the path and the file in separate parameters.
If you need the path of the inputfile to set it for your outputfiles, you can get it with these lines:
$tmpPath = Get-Childitem $InputFullPath
$Path = $tmpPath.Directory.FullName
Because you didn't explained what exactly this script should do, i hope you can use this to get what you wanted.
Greetings

Task scheduler creates corrupted version of generated by script file

I've been working on a little project in Powershell.
My task was to create a script that will collect all files from mail attachments, merge all .pdf files into one and send the generated file to my email.
The script works completely fine in Powershell ISE, but when I try to run it from task scheduler, the merged .pdf file is corrupted without any data in it.
Keep in mind I am new to this stuff.
This is my main code that does all the heavy work:
function getAttachments
{
#########################################################
##-----------------------------------------------------##
## GET ATTACHMENTS ##
##-----------------------------------------------------##
#########################################################
##PATH TO CREDENTIAL
$credpath = "C:\Users\" + $env:UserName + "\Documents\myCred_${env:USERNAME}_${env:COMPUTERNAME}.xml"
#test variable
$test = Test-Path $credpath
##TEST IF CREDENTIAL EXISTS
if(!$test){
## USER PROMPT PSW CREDENTIAL ##
$cred = Get-Credential
#save credential in documents
$cred | Export-CliXml -Path $credpath
}else{
##READ USER CREDENTIAL FROM FILE
$cred = Import-CliXml -Path $credpath
}
##url and date variables
$url = "https://outlook.office365.com/api/v1.0/me/messages"
$d = [DateTime]::Today.AddDays(-1)
$global:date = $d.ToString("yyyy-MM-dd")
## Get all messages that have attachments where received date is greater than $date
$messageQuery = "" + $url + "?`$select=Id&`$filter=HasAttachments eq true and DateTimeReceived ge " + $date
$messages = Invoke-RestMethod $messageQuery -Credential $cred
## Loop through each results
foreach ($message in $messages.value)
{
# get attachments and save to file system
$query = $url + "/" + $message.Id + "/attachments"
$attachments = Invoke-RestMethod $query -Credential $cred
# in case of multiple attachments in email
foreach ($attachment in $attachments.value)
{
Write-Host “Found File :- ” $attachment.Name
$path = "c:\Attachments\" + $attachment.Name
$Content = [System.Convert]::FromBase64String($attachment.ContentBytes)
Set-Content -Path $path -Value $Content -Encoding Byte
}
}
}
function sendAttachments
{
#############################################################
##---------------------------------------------------------##
## SEND ATTACHMENTS AND DELETE FILES ##
##---------------------------------------------------------##
#############################################################
#Connection Details
#PATH TO CREDENTIAL
$credpath = "C:\Users\" + $env:UserName + "\Documents\myCred_${env:USERNAME}_${env:COMPUTERNAME}.xml"
$cred = Import-CliXml -Path $credpath
$smtpServer = “ smtp.office365.com”
$msg = new-object Net.Mail.MailMessage
#Change port number for SSL to 587
$smtp = New-Object Net.Mail.SmtpClient($SmtpServer, 25)
#Uncomment Next line for SSL
$smtp.EnableSsl = $true
$smtp.Credentials = $cred
$msg.IsBodyHtml = $true
#From Address
$msg.From = $cred.UserName
#To Address, Copy the below line for multiple recipients
$msg.To.Add(“email#gmail.com”)
#Message Body
$msg.Body=”<h2>Alle attachments samen bevinden zich in de bijlage van did email</h2> <br/><br/>”
#Message Subject
$msg.Subject = “no-reply: Email met alle attachments”
#your file location
$files=Get-ChildItem “C:\Attachments\”
#attach the right file
$file = $global:pname
Write-Host “Attaching File :- ” $file
$attachment = New-Object System.Net.Mail.Attachment –ArgumentList C:\Attachments\$file
$msg.Attachments.Add($attachment)
#send email
$smtp.Send($msg)
$attachment.Dispose();
$msg.Dispose();
#delete the files from the folder
Get-ChildItem -Path C:\Attachments -Include * -File -Recurse | foreach { $_.Delete()}
}
function mergePDF
{
#############################################################
##---------------------------------------------------------##
## MERGE ALL PDF FILES ##
##---------------------------------------------------------##
#############################################################
$workingDirectory = "C:\Attachments"
$itspath = $PSScriptRoot
$global:pname = $global:date + "_pdfAttachments.pdf"
$pdfs = ls $workingDirectory -recurse | where {-not $_.PSIsContainer -and $_.Extension -imatch "^\.pdf$"};
[void] [System.Reflection.Assembly]::LoadFrom([System.IO.Path]::Combine($itspath, 'itextsharp.dll'));
$output = [System.IO.Path]::Combine($workingDirectory, $pname);
$fileStream = New-Object System.IO.FileStream($output, [System.IO.FileMode]::OpenOrCreate);
$document = New-Object iTextSharp.text.Document;
$pdfCopy = New-Object iTextSharp.text.pdf.PdfCopy($document, $fileStream);
$document.Open();
foreach ($pdf in $pdfs) {
$reader = New-Object iTextSharp.text.pdf.PdfReader($pdf.FullName);
[iTextSharp.text.pdf.PdfReader]::unethicalreading = $true
$pdfCopy.AddDocument($reader);
$reader.Dispose();
}
$document.Close()
$pdfCopy.Dispose();
$document.Dispose();
$fileStream.Dispose();
}
getAttachments
Start-Sleep -s 10
mergePDF
Start-Sleep -s 10
sendAttachments
In this piece of code that I run in another script file, I create a new task:
#############################################################
##---------------------------------------------------------##
## SCHEDULE SCRIPTS IN WINDOWS TASKS ##
##---------------------------------------------------------##
#############################################################
##PATH TO CREDENTIAL
$credpath = "C:\Users\" + $env:UserName + "\Documents\myCred_${env:USERNAME}_${env:COMPUTERNAME}.xml"
#test variable
$test = Test-Path $credpath
##TEST IF CREDENTIAL EXISTS
if(!$test){
## USER PROMPT PSW CREDENTIAL ##
$cred = Get-Credential
#save credential in documents
$cred | Export-CliXml -Path $credpath
}
$taskName = "ManageEmailAttachments"
$taskExists = Get-ScheduledTask | Where-Object {$_.TaskName -like $taskName }
if($taskExists)
{
Get-ScheduledJob ManageEmailAttachments
Unregister-ScheduledJob ManageEmailAttachments
$wshell = New-Object -ComObject Wscript.Shell
$wshell.Popup("Task successfully deleted, run the script again to schedule the task",0,"Done",0x0)
}
else
{
$tt = Get-Date
$tt = $tt.AddMinutes(1)
$testtime = $tt.ToString("HH:mm:ss")
#set trigger
$trigger = New-JobTrigger -Daily -At "1:00"
$testtrigger = New-JobTrigger -Daily -At $testtime
#path to the scripts
$scriptPath = $PSScriptRoot + "\wps_manage_pdf_attachments.ps1"
#options(optional)
$option = New-ScheduledJobOption -WakeToRun: $true
#create a new task
Register-ScheduledJob -Name ManageEmailAttachments -FilePath $scriptPath -Trigger $testtrigger -ScheduledJobOption $option
}
The script when run in Powershell works great, it gets all the attachments from mailbox, merges them into 1 .pdf file and sends them to the requested email address. But when scheduled in windows task scheduler it does the first step fine, but when merged, the .pdf file is corrupted without any content.
I couldn't figure out how to make it work so I posted a question on the forum.
Hope you guys find a way to figure it out.
Thanks in advance
Use below function to get script root directory.
Function Get-ScriptDirectory
{
$Invocation = (Get-Variable MyInvocation -scope 1).Value
Split-path $Invocation.MyCommand.Path
}
$scriptPath=Join-Path(Get-ScriptDirectory) 'wps_manage_pdf_attachments.ps1'
Apparently the problem nested itself in the main code. I used:
Try{...}
Catch{$_ | Out-File C:\errors.txt}
In mergePDF function to find out what the error was. Seems like the path to my ITextSharp.dll was incorrect. $PSScriptRoot that I used showed "C:\windows\windows32" instead of where the script actually is.
So what I did instead was add a line in my batch file to copy the itextsharp.dll to %Temp%:
xcopy Scripts\itextsharp.dll %Temp% /D >NUL 2>NUL
and then read the file from there with:
$itsPath = [System.IO.Path]::GetTempPath()
And everything works as it should be. I know this isn't the best way to do it but I had this batch file before to make the script run by just dubbleclicking it.
So adding a little line won't hurt.
I hope this helps anyone with the same problem.

how to extract test log from MTM ?

I'm testing a project and all my test cases are in MTM , I'm looking for a way to extract all the test result we have in the MTM in a separate file , is there any way to do that ? please share if you have any idea
thanks a lot
If you wish to export the results of an automated run, you can download the .trx (test run execution) file from the attachments section and use XSLand XSLT to create an html report from it (you can also use the command-line tool tcm.exe run /export to get a .trx file).
But if you created the test run by manual execution, this won't be possible. The only way to get a "result file" would be to parse the result of the test run using the TFS API (in C# or Powershell via TfsTeamProjectCollection from Microsoft.TeamFoundation.TestManagement.Client and store it in a file.
Or you can use the TFS Rest-API with this PowerShell-Script (save as .ps) which lets you query a JSON and extract the data you want and display it the way you want to:
$RunId = Read-Host -Prompt "TFS Run Id"
$Url = "http://<tfsurl>/tfs/<CollectionName>/<TeamProject>/_apis/test/runs/$RunId/results"
$Client = New-Object System.Net.WebClient
$Client.Credentials = New-Object System.Net.NetworkCredential("<username>", "<password>", "<domain>")
$Json = $Client.DownloadString($Url) | ConvertFrom-Json
$Dict = #{}
ForEach($Test in $Json.value)
{
$Key = "Run " + $Test.testRun.name + " [" + $Test.testRun.id + "]"
$Val = $Test.testCase.name + " [" + $Test.testCase.id + "]" + " = " + $Test.outcome
if (!$Dict.ContainsKey($Key))
{
$List = New-Object System.Collections.ArrayList
$Dict.Add($Key, $List)
}
$IgnoreIndex = $Dict[$Key].Add($Val)
}
ForEach($Key in $Dict.Keys)
{
Write-Host $Key
ForEach($Val in $Dict[$Key])
{
Write-Host $Val
}
}
Exit
(replace values like <xxx> with yours)

Powershell DBCC CheckDB from Powershell

I was wondering if anyone could help with the following code shown. I'm basically trying to get this code re-hashed if possible to allow me to run it against a set of server names supplied in a text file named "servers.txt".
The DBCC should be run by the PS script and run against all DB for that servername. I'm not up to speed enough with PS to understand how to do this for this script.
How to change it allow to plug in values instead of being hardcoded for each servername?
I've read a bit around this and looked at the Invoke-Sql command which I believe is a SQL 2008 extension to PS.
Unfortunately the PS environment is from a SQL 2005 box and I dont have the power to get this moved so dont think ill be able to use invoke
Please see the original code and then my experiment to try and get it to run using invoke.
$ScriptName = $myInvocation.MyCommand.Name
[void][reflection.assembly]::LoadWithPartialName("System.Data.SqlClient")
$ConnString = "Server=DB-OCC05;Integrated Security=SSPI;Database=master;Application Name=$ScriptName"
$MasterConn = new-object ('System.Data.SqlClient.SqlConnection') $ConnString
$MasterCmd = new-object System.Data.SqlClient.SqlCommand
$MasterCmd.Connection = $MasterConn
$SqlDBCC = "DBCC CHECKDB(master) WITH TABLERESULTS"
$MasterCmd.CommandText = $SqlDBCC
$MasterConn.Open()
$Rset = $MasterCmd.ExecuteReader()
If ($Rset.HasRows -eq $true) {
While ($Rset.Read()) {
$line = $Rset["MessageText"]
If ($Rset["Level"] -gt 10) {
Write-Host $line -backgroundcolor Yellow -foregroundcolor Red
} else {
Write-Host $line
}
}
$Rset.Close()
}
$MasterConn.Close()
And then my test running from SQL 2005 environment:
Invoke-Sqlcmd -Query "SELECT GETDATE() AS TimeOfQuery;" -ServerInstance "MyComputer\MyInstance"
And also tried this test:
gc "C:\Powershell\Servers.txt" | foreach-object {Invoke-Sqlcmd "DBCC checkdb;" -ServerInstance "$_\MyInstance"}
But the above test runs didnt work cause of the:
The term 'Invoke-Sqlcmd' 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.
A few modifications to your script. Everything is basically the same except for the connection string and the few lines at the bottom for loading your servers.txt file (a text file with one line per instance) and enumerating its content:
function Execute-DBCC
{
param (
[parameter(Mandatory = $true)][string]$serverInstance
)
$connString = "Server=$serverInstance;Integrated Security=SSPI;Database=master;Application Name=$ScriptName"
$masterConn = new-object ('System.Data.SqlClient.SqlConnection') $connString
$masterCmd = new-object System.Data.SqlClient.SqlCommand
$masterCmd.Connection = $masterConn
$masterCmd.CommandText = "EXECUTE master.sys.sp_MSforeachdb 'DBCC CHECKDB([?]) WITH TABLERESULTS'"
$masterConn.Open()
$reader = $masterCmd.ExecuteReader()
if ($reader.HasRows -eq $true)
{
while ($reader.Read())
{
$messageText = $reader["MessageText"]
if ($reader["Level"] -gt 10)
{ Write-Host $messageText -backgroundcolor Yellow -foregroundcolor Red }
else
{ Write-Host $messageText }
}
$reader.Close()
}
$masterConn.Close()
}
[void][reflection.assembly]::LoadWithPartialName("System.Data.SqlClient")
$servers = #(Get-Content ".\servers.txt")
$servers | %{
Execute-DBCC $_
}

Resources