I am trying to create a backup of a SQL stored procedure using PowerShell, but it produces a blank file. It's not throwing an error.
Here is my code:
param([String]$step='exeC dbo.test',[String]$sqlfile='',[String]$servename = 'test',[String]$dbname = 'test')
$step2=$step
$step3=$step2.Replace('[','')
$step4 = $step3.Replace(']','')
$step4 = $step4.Split(" ")[1]
$step5 = $step4.Split(".")
Write-Output $step5[0,1]
[System.Reflection.Assembly]::LoadWithPartialName(“Microsoft.SqlServer.SMO”) | out-null
$logfolder = 'C:\Users\fthoma15\Documents\sqlqueries\Logs'
$bkupfolder = 'C:\Users\fthoma15\Documents\sqlqueries\Backup'
$statsfolder = 'C:\Users\fthoma15\Documents\sqlqueries\stats'
$SMOserver = new-object ("Microsoft.SqlServer.Management.Smo.Scripter") #-argumentlist $server
$srv = new-Object Microsoft.SqlServer.Management.Smo.Server("$servename")
#Prompt for user credentials
$srv.ConnectionContext.LoginSecure = $false
$credential = Get-Credential
#Deal with the extra backslash character
$loginName = $credential.UserName -replace("\\","")
#This sets the login name
$srv.ConnectionContext.set_Login($loginName);
#This sets the password
$srv.ConnectionContext.set_SecurePassword($credential.Password)
$srv.ConnectionContext.ApplicationName="MySQLAuthenticationPowerShell"
#$srv.Databases | Select name
$db = New-Object Microsoft.SqlServer.Management.Smo.Database
$db = $srv.Databases.Item("$dbname")
#$db.storedprocedures | Select name
$Objects = $db.storedprocedures[$step5[1,0]]
#Write-Output $step5[1,0]
#Write-Output $Objects
$scripter = new-object ("$SMOserver") $srv
$Scripter.Script($Objects) | Out-File $bkupfolder\backup_$($step5[1]).sql
Please help
This was an issue with permission to the database. I gave the SQL id permission to the database and now it works.
The error received is "The SqlParameterCollection only accepts non-null SqlParameter type objects, not SqlCommand objects." & "Procedure or function 'usp__SingleUpdateServerBackupPath' expects parameter '#decServerName', which
was not supplied."
PowerShell code:
Set-StrictMode -Version 1.0
function update-serverbackuppath {
param(
[Parameter(Mandatory=$True,ValueFromPipeLine=$True)][object[]]$inputobject
)
BEGIN {
$connection = New-Object -TypeName System.Data.SqlClient.SqlConnection
$connection.ConnectionString = "server=servername;database=database;trusted_connection=yes"
$connection.Open()
}
PROCESS {
$UpdateBackupPath = New-Object -TypeName System.Data.SqlClient.SqlCommand
$UpdateBackupPath.Connection = $connection
$UpdateBackupPath.CommandText = "usp__SingleUpdateServerBackupPath"
$UpdateBackupPath.Commandtype = [System.Data.Commandtype]::StoredProcedure
$ParamUpdateBackupPath = New-Object -TypeName System.Data.SqlClient.SqlParameter
$ParamUpdateBackupPath.ParameterName = "#decBackupPath"
$ParamUpdateBackupPath.SqlDbType = [System.Data.SqlDbType]::VarChar
$ParamUpdateBackupPath.Direction = [System.Data.ParameterDirection]::Input
$ParamUpdateBackupPath.Value = $inputobject.paths
$ParamUpdateBackupPathServerName = New-Object -TypeName System.Data.SqlClient.SqlCommand
$ParamUpdateBackupPathServerName.ParameterName = "#decServerName"
$ParamUpdateBackupPathServerName.SqlDbType = [System.Data.SqlDbType]::VarChar
$ParamUpdateBackupPathServerName.Direction = [System.Data.ParameterDirection]::Input
$ParamUpdateBackupPathServerName.Value = $inputobject.names
$UpdateBackupPath.Parameters.Add($ParamUpdateBackupPath)
$UpdateBackupPath.Parameters.Add($ParamUpdateBackupPathServerName)
$reader = New-Object -TypeName System.Data.SqlClient.SqlDataReader = $UpdateBackupPath.ExecuteReader()
}
END {
$connection.Close()
}
}
SQL Procedure:
Create Procedure usp__SingleUpdateServerBackupPath
(
#decBackupPath AS varchar(50),
#decServerName AS varchar(50)
)
AS
UPDATE BCKP
SET PTH = #decBackupPath
FROM BCKP
INNER JOIN SRVR
ON SRVR.ID = BCKP.FK_SRVR
WHERE SRVR.NM = #decServerName
CSV File Format
Import-Csv -Path C:\Bin\Repos\Backup.csv | C:\Bin\Scripts\update-serverbackuppath.ps1
Names Paths
Server1 \\fileshare\server_name
The Powershell code has several syntax errors, like referring to enums in erroneus a way:
# Incorrect form
[System.Data.Commandtype.StoredProcedure]
# Correct form for referring to enumeration
[System.Data.Commandtype]::StoredProcedure
Later on, there is an undeclared object which's member method is called:
# $command is not set, so ExecuteReader method is available
$reader = $command.ExecuteReader()
It is highly recommended to use strict mode in Powershell. It helps catching typos by preventing access to non-existing properties an uninitialized variables.
Edit
After the updated code, there are still two errors:
# This doesn't make sense. The variable should be SqlParameter, not SqlCommand
$ParamUpdateBackupPathServerName = New-Object -TypeName System.Data.SqlClient.SqlCommand
# Like so:
$ParamUpdateBackupPathServerName = New-Object -TypeName System.Data.SqlClient.SqlParameter
# This is nonsense syntax
$reader = New-Object -TypeName System.Data.SqlClient.SqlDataReader = $UpdateBackupPath.ExecuteReader()
# Like so:
$reader = $UpdateBackupPath.ExecuteReader()
I am trying to create a script to automatically iterate through a text file of all our SQL Server instances and add each on if it doesn't already exist to the CMS. I want to try doing this through SMO instead of hardcoding sql strings in. Below is what I have so far but it doesn't seem to be working. Any help would be appreciated. Thanks.
Eventually I will add more If statements in to distribute the instances to certain groups but for now I'm just trying to get it to populate everything.
$CMSInstance = "cmsinstancename"
$ServersPath = "C:\Scripts\InPutFiles\servers.txt"
#Load SMO assemplies
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | out-null
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.Management.RegisteredServers') | out-null
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.Management.Common') | out-null
$connectionString = "Data Source=$CMSINstance;Initial Catalog=master;Integrated Security=SSPI;"
$sqlConnection = new-object System.Data.SqlClient.SqlConnection($connectionString)
$conn = new-object Microsoft.SqlServer.Management.Common.ServerConnection($sqlConnection)
$CMSStore = new-object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServersStore($conn)
$CMSDBStore = $CMSStore.ServerGroups["DatabaseEngineServerGroup"]
$Servers = Get-Content $ServersPath;
foreach($Server in $Servers)
{
#Put this in loop to deal with duplicates in list itself
$AlreadyRegisteredServers = #()
$CMSDBStore.GetDescendantRegisteredServers()
$RegServerName = $Server.Name
$RegServerInstance = $Server.Instance
if($AlreadyRegisteredServers -notcontains $RegServerName)
{
Write-Host "Adding Server $RegServerName"
$NewServer = New-Object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServer($CMSDBStore, "$RegServerName")
$NewServer.SecureConnectionString = "server=$RegServerInstance;integrated security=true"
$NewServer.ConnectionString = "server=$RegServerInstance;integrated security=true"
$NewServer.ServerName = "$RegServerInstance"
$NewServer.Create()
}
else
{
Write-Host "Server $RegServerName already exists - cannot add."
}
}
I cut your script down to just the basics and it works for me. I did have to change the connection command to work in my environment but other than that and registering a default instance of SQL Server there were no errors. Once I did a refresh of the CMS server the newly registered server was visible and accessible.
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.Management.RegisteredServers') | Out-Null
$CMSInstance = 'CMS_ServerName'
$connectionString = "Data Source=$CMSInstance;Initial Catalog=master;Integrated Security=SSPI;"
$sqlConnection = new-object System.Data.SqlClient.SqlConnection($connectionString)
$conn = New-Object System.Data.SqlClient.SqlConnection("Server=$CMSInstance;Database=master;Integrated Security=True")
$CMSStore = new-object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServersStore($conn)
$CMSDBStore = $CMSStore.ServerGroups["DatabaseEngineServerGroup"]
$RegServerName = 'ServerToRegister'
$RegServerInstance = $RegServerName
$NewServer = New-Object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServer($CMSDBStore, "$RegServerName")
$NewServer.SecureConnectionString = "server=$RegServerInstance;integrated security=true"
$NewServer.ConnectionString = "server=$RegServerInstance;integrated security=true"
$NewServer.ServerName = "$RegServerInstance"
$NewServer.Create()
I have the follow snippet a powershell script and the nested if statements are not being executed. Can you tell me why ? I've recently got back into the programming game so its probably something simple
$false.
$IgnoreThisLine = $false
$tmpString = "(DATUM CTR OF BLADE)"
$EndOfDescription = $null
$cncPrograms ="(165756 SIDE 1)"
if ($IgnoreThisLine = $false ) {
If ($tmpString -match '\(') {
$EndOfDescription = $false
$cncPrograms = $tmpString
}
else {
$EndOfDescription = $true
}
}
if ($IgnoreThisLine -eq $false)
"-eq" not "=" to test for equality with PowerShell.
I want to create an ISO image, so a .iso file, on Windows. This is possible to do using COM component IMAPI2FS.MsftFileSystemImage, and I found instructions on how to do this using PowerShell in an MSDN blog post entitled "Writing optical discs using IMAPI 2 in powershell".
After step 3, those instructions say that "at this step you can stop and save resulted image to the local hard disc, this will be a pure iso image."
My question: How do I take $resultStream, i.e., a COM object that results from retrieving an ImageStream, in PowerShell and save its contents to a file?
You need to use FileStream writer. Check this link for an example of how it is done in c#.
http://tools.start-automating.com/Install-ExportISOCommand/?-Download
The function there can be used to create cmdlets that help you create ISO. For example,
Run Install-ExportISOCommand
This creates Export-Iso
Then, use Export-ISO to create an ISO.
Export-Iso -ISOPath C:\dropbox\test.iso -FileName C:\Dropbox\Scripts
You can use the DiscUtils library to make ISOs (and manage other disk formats such as VHD/VHDX, DMG, FAT, etc). A PowerShell module is supported as well an MSBUILD task to automatically create your ISO on project build.
Create a CDBuilder object and go to town with adding files and directories then save it to disk with the Build method. Download documentation here.
CDBuilder builder = new CDBuilder();
builder.AddFile("samplefile.txt", new byte[] { });
builder.Build(#"c:\output.iso");
The great thing about this approach is that it is 100% managed code and cross-platform - there is no IMAPI2 COM/Marshaling requirement.
This is an end-to-end PowerShell ISO creator with GUI that I've used many times. No additional software required.
# Author: Hrisan Dzhankardashliyski
# Date: 20/05/2015
# Inspiration from
#
# http://blogs.msdn.com/b/opticalstorage/archive/2010/08/13/writing-optical-discs-using-imapi-2-in-powershell.aspx</a>
#
# and
#
# http://tools.start-automating.com/Install-ExportISOCommand/</a>
#
# with help from
#
# http://stackoverflow.com/a/9802807/223837</a>
$InputFolder = ""
function WriteIStreamToFile([__ComObject] $istream, [string] $fileName)
{
# NOTE: We cannot use [System.Runtime.InteropServices.ComTypes.IStream],
# since PowerShell apparently cannot convert an IStream COM object to this
# Powershell type. (See http://stackoverflow.com/a/9037299/223837 for
# details.)
#
# It turns out that .NET/CLR _can_ do this conversion.
#
# That is the reason why method FileUtil.WriteIStreamToFile(), below,
# takes an object, and casts it to an IStream, instead of directly
# taking an IStream inputStream argument.
$cp = New-Object CodeDom.Compiler.CompilerParameters
$cp.CompilerOptions = "/unsafe"
$cp.WarningLevel = 4
$cp.TreatWarningsAsErrors = $true
Add-Type -CompilerParameters $cp -TypeDefinition #"
using System;
using System.IO;
using System.Runtime.InteropServices.ComTypes;
namespace My
{
public static class FileUtil {
public static void WriteIStreamToFile(object i, string fileName) {
IStream inputStream = i as IStream;
FileStream outputFileStream = File.OpenWrite(fileName);
int bytesRead = 0;
int offset = 0;
byte[] data;
do {
data = Read(inputStream, 2048, out bytesRead);
outputFileStream.Write(data, 0, bytesRead);
offset += bytesRead;
} while (bytesRead == 2048);
outputFileStream.Flush();
outputFileStream.Close();
}
unsafe static private byte[] Read(IStream stream, int toRead, out int read) {
byte[] buffer = new byte[toRead];
int bytesRead = 0;
int* ptr = &bytesRead;
stream.Read(buffer, toRead, (IntPtr)ptr);
read = bytesRead;
return buffer;
}
}
}
"#
[My.FileUtil]::WriteIStreamToFile($istream, $fileName)
}
# The Function defines the ISO parameturs and writes it to file
function createISO([string]$VolName,[string]$Folder,[bool]$IncludeRoot,[string]$ISOFile){
# Constants from http://msdn.microsoft.com/en-us/library/windows/desktop/aa364840.aspx
$FsiFileSystemISO9660 = 1
$FsiFileSystemJoliet = 2
$FsiFileSystemUDF = 4
$fsi = New-Object -ComObject IMAPI2FS.MsftFileSystemImage
#$fsi.FileSystemsToCreate = $FsiFileSystemISO9660 + $FsiFileSystemJoliet
$fsi.FileSystemsToCreate = $FsiFileSystemUDF
#When FreeMediaBlocks is set to 0 it allows the ISO file to be with unlimited size
$fsi.FreeMediaBlocks = 0
$fsi.VolumeName = $VolName
$fsi.Root.AddTree($Folder, $IncludeRoot)
WriteIStreamToFile $fsi.CreateResultImage().ImageStream $ISOFile
}
Function Get-Folder($initialDirectory)
{
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")
$foldername = New-Object System.Windows.Forms.FolderBrowserDialog
$foldername.rootfolder = "MyComputer"
if($foldername.ShowDialog() -eq "OK")
{
$folder += [string]$foldername.SelectedPath
}
return $folder
}
# Show an Open Folder Dialog and return the directory selected by the user.
function Read-FolderBrowserDialog([string]$Message, [string]$InitialDirectory, [switch]$NoNewFolderButton)
{
$browseForFolderOptions = 0
if ($NoNewFolderButton) { $browseForFolderOptions += 512 }
$app = New-Object -ComObject Shell.Application
$folder = $app.BrowseForFolder(0, $Message, $browseForFolderOptions, $InitialDirectory)
if ($folder) { $selectedDirectory = $folder.Self.Path }
else { $selectedDirectory = '' }
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($app) > $null
return $selectedDirectory
}
#Prompts the user to save the ISO file, if the files does not exists it will create it otherwise overwrite without prompt
Function Get-SaveFile($initialDirectory)
{
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") |
Out-Null
$SaveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
$SaveFileDialog.CreatePrompt = $false
$SaveFileDialog.OverwritePrompt = $false
$SaveFileDialog.initialDirectory = $initialDirectory
$SaveFileDialog.filter = "ISO files (*.iso)| *.iso"
$SaveFileDialog.ShowHelp = $true
$SaveFileDialog.ShowDialog() | Out-Null
$SaveFileDialog.filename
}
# Show message box popup and return the button clicked by the user.
function Read-MessageBoxDialog([string]$Message, [string]$WindowTitle, [System.Windows.Forms.MessageBoxButtons]$Buttons = [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]$Icon = [System.Windows.Forms.MessageBoxIcon]::None)
{
Add-Type -AssemblyName System.Windows.Forms
return [System.Windows.Forms.MessageBox]::Show($Message, $WindowTitle, $Buttons, $Icon)
}
# GUI interface for the PowerShell script
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") #loading the necessary .net libraries (using void to suppress output)
$Form = New-Object System.Windows.Forms.Form #creating the form (this will be the "primary" window)
$Form.Text = "ISO Creator Tool:"
$Form.Size = New-Object System.Drawing.Size(600,300) #the size in px of the window length, height
$Form.FormBorderStyle = 'FixedDialog'
$Form.MaximizeBox = $false
$Form.MinimizeBox = $false
$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(20,20)
$objLabel.Size = New-Object System.Drawing.Size(120,20)
$objLabel.Text = "Please select a Folder:"
$Form.Controls.Add($objLabel)
$InputBox = New-Object System.Windows.Forms.TextBox
$InputBox.Location = New-Object System.Drawing.Size(150,20)
$InputBox.Size = New-Object System.Drawing.Size(300,20)
$InputBox.Enabled = $false
$Form.Controls.Add($InputBox)
$objLabel2 = New-Object System.Windows.Forms.Label
$objLabel2.Location = New-Object System.Drawing.Size(20,80)
$objLabel2.Size = New-Object System.Drawing.Size(120,20)
$objLabel2.Text = "ISO File Name:"
$Form.Controls.Add($objLabel2)
$InputBox2 = New-Object System.Windows.Forms.TextBox
$InputBox2.Location = New-Object System.Drawing.Size(150,80)
$InputBox2.Size = New-Object System.Drawing.Size(300,20)
$InputBox2.Enabled = $false
$Form.Controls.Add($InputBox2)
$objLabel3 = New-Object System.Windows.Forms.Label
$objLabel3.Location = New-Object System.Drawing.Size(20,50)
$objLabel3.Size = New-Object System.Drawing.Size(120,20)
$objLabel3.Text = "ISO Volume Name:"
$Form.Controls.Add($objLabel3)
$InputBox3 = New-Object System.Windows.Forms.TextBox
$InputBox3.Location = New-Object System.Drawing.Size(150,50)
$InputBox3.Size = New-Object System.Drawing.Size(150,20)
$Form.Controls.Add($InputBox3)
$objLabel4 = New-Object System.Windows.Forms.Label
$objLabel4.Location = New-Object System.Drawing.Size(20,120)
$objLabel4.Size = New-Object System.Drawing.Size(120,20)
$objLabel4.Text = "Status Msg:"
$Form.Controls.Add($objLabel4)
$InputBox4 = New-Object System.Windows.Forms.TextBox
$InputBox4.Location = New-Object System.Drawing.Size(150,120)
$InputBox4.Size = New-Object System.Drawing.Size(200,20)
$InputBox4.Enabled = $false
$InputBox4.Text = "Set ISO Parameters..."
$InputBox4.BackColor = "LightGray"
$Form.Controls.Add($InputBox4)
$Button = New-Object System.Windows.Forms.Button
$Button.Location = New-Object System.Drawing.Size(470,20)
$Button.Size = New-Object System.Drawing.Size(80,20)
$Button.Text = "Browse"
$Button.Add_Click({
$InputBox.Text=Read-FolderBrowserDialog
$InputBox4.Text = "Set ISO Parameters..."
})
$Form.Controls.Add($Button)
$Button2 = New-Object System.Windows.Forms.Button
$Button2.Location = New-Object System.Drawing.Size(470,120)
$Button2.Size = New-Object System.Drawing.Size(80,80)
$Button2.Text = "CreateISO"
$Button2.Add_Click({
if(($InputBox.Text -eq "") -or ($InputBox3.Text -eq "")){
Read-MessageBoxDialog "You have to select folder and specify ISO Volume Name" "Error: No Parameters entered!"
} else{
$SaveDialog = Get-SaveFile
#If you click cancel when save file dialog is called
if ($SaveDialog -eq ""){
return
}
$InputBox2.Text= $SaveDialog
$InputBox2.Refresh()
if($checkBox1.Checked){
$includeRoot=$true
}
else{
$includeRoot=$false
}
$InputBox4.BackColor = "Yellow"
$InputBox4.Text = "Generating ISO file..."
$InputBox4.Refresh()
createISO $InputBox3.Text $InputBox.Text $includeRoot $InputBox2.Text
$InputBox4.BackColor = "LimeGreen"
$InputBox4.Text = "ISO Creation Finished!"
$InputBox4.Refresh()
}
})
$Form.Controls.Add($Button2)
$objLabel5 = New-Object System.Windows.Forms.Label
$objLabel5.Location = New-Object System.Drawing.Size(20,160)
$objLabel5.Size = New-Object System.Drawing.Size(280,20)
$objLabel5.Text = "Check the box if you want to include the top folder:"
$Form.Controls.Add($objLabel5)
$checkBox1 = New-Object System.Windows.Forms.CheckBox
$checkBox1.Location = New-Object System.Drawing.Size(300,156)
$Form.Controls.Add($checkBox1)
$Form.Add_Shown({$Form.Activate()})
[void] $Form.ShowDialog()