At the time of writing SQL Server 2008 is currently at the Community Technology Preview (CTP) stage. CTP 6 was delivered in February 2008 and includes a PowerShell provider. In this article I will explain what PowerShell is, why a PowerShell provider is useful and provide some examples of how you can use PowerShell with SQL Server.
PowerShell is the .NET based automation engine that Microsoft shipped in November 2006. It can have a MMC layered over the top as in Exchange 2007, it can be embedded into .NET applications but is usually used as a command line shell and scripting language. There has been over 2 million downloads of the PowerShell install package since it became available. PowerShell install packages are available in 32 bit and 64 bit versions for Windows 2003, Windows XP and Windows Vista. It is an installable feature in Windows Server 2008 i.e. it is part of the operating system install. It is incorporated into a number of Microsoft products including Exchange 2007, a number of System Center products including Operations Manager 2007, Data Protection Manager 2007 and Virtual Machine Manager. A number of third party vendors including Quest, Special Operations Software, SDM software, IBM, Citrix and VMware have produced PowerShell adds for their products or incorporated PowerShell directly into their products. PowerShell is now part of Microsoft’s Common Engineering Criteria and will be incorporated into all major products.
A second CTP of PowerShell version 2 was made available in May 2008. This includes some major enhancements to the functionality. PowerShell V2 is a complete replacement for PowerShell V1 and is not currently compatible with all applications that use PowerShell.
PowerShell has a number of features that need to be understood before working with it:
SQL Server 2005 saw the introduction of Server Management Objects (SMO). As these objects are .NET objects they can be utilised by PowerShell. Install the SQL Server 2005 tools on a machine with PowerShell, load the SMO assemblies into PowerShell and the functionality is available for use in scripts. As an example this script lists the databases available on a server.
## load SMO assemblies $null = [reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.ConnectionInfo") $null = [reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.SmoEnum") $null = [reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") ## set SMO variable $Smo = "Microsoft.SqlServer.Management.Smo." $server = new-object ($Smo + 'server') "SQL1" $server.databases | Select Name | Format-Table
## load SMO assemblies
$null = [reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.ConnectionInfo")
$null = [reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.SmoEnum")
$null = [reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")
## set SMO variable
$Smo = "Microsoft.SqlServer.Management.Smo."
$server = new-object ($Smo + 'server') "SQL1"
$server.databases | Select Name | Format-Table
We need to start by loading the SMO assemblies. In the script anything that starts with $ is a variable. Using $null when loading the assemblies suppresses the load messages. Setting
enables us to reuse this if the script is extended (saves a bit of typing!). SMO is a hierarchy of objects with the server object being the topmost object in the hierarchy. The new-object cmdlet is used to create an instance of the server object. The server object has as a property a collection of the databases on that server. This is accessed as shown on the last line of the script.
We pass the collection of databases onto the pipeline. We then select just the database name and then format the data and display in a table. Note that Select is an alias for the Select-Object cmdlet.
With SQL Server 2008 we still have the ability to use this functionality. In addition we have native support for PowerShell through the PowerShell provider that is installed, some SQL Server specific cmdlets and that fact that it is possible to use PowerShell in SQL Server Agent job steps. The use of PowerShell in SQL Server jobs will not be covered in this article. PowerShell (and .NET 2.0) is now a pre-requisite for the installation of SQL Server 2008. If you install SQL Server 2008 onto Windows Server 2008 the PowerShell feature will be automatically installed.
The SQL Server team have approached PowerShell support in a different manner to other Microsoft products. They have chosen to create a SQL Server specific version of PowerShell in which the SQL Server functionality is pre-installed. This means that there will be two versions of PowerShell on any machine with SQL Server 2008 installed:
There are a number of points that must be remembered regarding the SQL Server PowerShell console:
The SQL Server PowerShell console is closed. It is not possible to use Add-PSSnapin to add additional functionality.
A PowerShell provider exposes a data store – in this case the SQL Server SMO object hierarchy – as if it was the file system this means that we can navigate around it as if it was the file system using cd etc. PowerShell expands on the concept of file system drives to expose the data stores.
Let’s start by navigating into the provider. There are actually two providers. The other exposes SQL Server 2008 policies. That one will be left for another article.
cd SQL: dir
cd SQL:
dir
We will see the available machines.
cd sql2008 dir
cd sql2008
We start by getting the server object. Compare this to how we did it in the script. Get-Item is a standard cmdlet for working with providers. We would use exactly the same cmdlet to access a file in the file system.
$server = get-item default
Once we have the server object we can start to work with it. These are the standard properties exposed in SQL Server. We can see the same properties in the SQL Server Management Studio.
$Server.Information.Properties | Select-Object Name, Value | Format-Table -auto cls $Server.Settings.Properties | Select-Object Name, Value | Format-Table -auto cls $Server.UserOptions.Properties | Select-Object Name, Value | Format-Table -auto cls $Server.Configuration.Properties | Select-Object DisplayName, ConfigValue, RunValue, Description | Format-Table -auto
$Server.Information.Properties | Select-Object Name, Value | Format-Table -auto
cls
$Server.Settings.Properties | Select-Object Name, Value | Format-Table -auto
$Server.UserOptions.Properties | Select-Object Name, Value | Format-Table -auto
$Server.Configuration.Properties | Select-Object DisplayName, ConfigValue, RunValue, Description | Format-Table -auto
In each case we are selecting particular properties to view and then displaying the information in a table. The –auto parameter automatically adjusts the column widths to fit the console width.
Still working with the server object we can easily view database information.
$server.Databases
To view database size information we need a little piece of code. We start by creating a header row. The `n tells PowerShell to throw a blank line before displaying the data. The syntax is PowerShell’s usage of .NET formatted strings. The parts in the {} determine the content and the width of the field so {0,20} means take the first piece of data after the –f and put it into a right justified field 20 characters wide.
We then loop through all of the databases displaying the name, the size, the space available, the log size and the log used space. Notice we are dividing by 1KB – that does mean kilobytes. PowerShell interprets KB, MB and GB as Kilobyte, Megabyte and Gigabyte respectively and can work with them as values in calculations.
"`n {0,20} {1,15} {2,15} {3,15} {4,15}" -f "Name", "Size", "Available", "Log size", "Log Used"; foreach ($db in $Server.Databases) {"`n {0,20} {1,15:n} {2,15:n} {3,15:n} {4,15:n}" -f $db.Name, $db.Size, $($db.SpaceAvailable/1KB), $($db.LogFiles[0].Size/1KB), $($db.LogFiles[0].UsedSpace/1KB)}
"`n {0,20} {1,15} {2,15} {3,15} {4,15}" -f "Name", "Size", "Available", "Log size", "Log Used";
foreach ($db in $Server.Databases) {"`n {0,20} {1,15:n} {2,15:n} {3,15:n} {4,15:n}" -f $db.Name, $db.Size, $($db.SpaceAvailable/1KB), $($db.LogFiles[0].Size/1KB), $($db.LogFiles[0].UsedSpace/1KB)}
One of the restrictions of the SQL Server provider is that we cannot create objects using the normal provider methods. We need to resort to code. These steps could be run interactively or could be put into a script. As it stands the code will create a database with all of the default settings.
$Server = new-object Microsoft.SqlServer.Management.Smo.Server("SQL2008") #$db = New-Object Microsoft.SqlServer.Management.Smo.Database($server, "TestDB") #$db.Create()
$Server = new-object Microsoft.SqlServer.Management.Smo.Server("SQL2008")
#$db = New-Object Microsoft.SqlServer.Management.Smo.Database($server, "TestDB")
#$db.Create()
This takes us into the default SQL Server instance on the machine.
cd default dir
cd default
We can move into the databases. Notice that it is case sensitive!
cd Databases
We can view the mounted databases
dir | Select Name | Format-Table Compare this to what we were doing at the server level. To view the system databases and see who has sysadmins Get-Item . cd master Invoke-Sqlcmd -Query "SELECT * FROM syslogins" | Where{$_.Sysadmin -eq 1} | Select Loginname
dir | Select Name | Format-Table
Compare this to what we were doing at the server level.
To view the system databases and see who has sysadmins
Get-Item .
cd master
Invoke-Sqlcmd -Query "SELECT * FROM syslogins" | Where{$_.Sysadmin -eq 1} | Select Loginname
At this point we are in the Master database so we need to move back up a level – again exactly as if we were navigating through a folder hierarchy.
cd ..
We have created a database – now we will create a table in the Adventureworks database.
$script = New-Object -Type System.Collections.Specialized.StringCollection $script.Add("SET ANSI_NULLS On") $script.Add("SET QUOTED_IDENTIFIER ON") $script.Add("CREATE TABLE [dbo].[Test1]([TestID] [int] NOT NULL, [Col1] [int] NOT NULL, [Col2] [int] NOT NULL) ON [PRIMARY]") $db = $server.Databases["AdventureWorks"] $extype = [Microsoft.SqlServer.Management.Common.ExecutionTypes]::ContinueOnError $db.ExecuteNonQuery($script, $extype)
$script = New-Object -Type System.Collections.Specialized.StringCollection
$script.Add("SET ANSI_NULLS On")
$script.Add("SET QUOTED_IDENTIFIER ON")
$script.Add("CREATE TABLE [dbo].[Test1]([TestID] [int] NOT NULL, [Col1] [int] NOT NULL, [Col2] [int] NOT NULL) ON [PRIMARY]")
$db = $server.Databases["AdventureWorks"]
$extype = [Microsoft.SqlServer.Management.Common.ExecutionTypes]::ContinueOnError
$db.ExecuteNonQuery($script, $extype)
In effect we are putting together the TSQL we would use and then running it. To view the table we navigate into the database.
cd AdventureWorks dir Tables cd Tables cd dbo.Test1 dir Columns
cd AdventureWorks
dir Tables
cd Tables
cd dbo.Test1
dir Columns
A table is no use without some data. As well as the provider there are a handful of cmdlets provided with SQL Server. One of them allows us to run SQL queries
Invoke-Sqlcmd -Database AdventureWorks -Query "Insert INTO dbo.test1 values (1,2,3), (4,5,6)" Invoke-Sqlcmd -Database AdventureWorks -Query "Select * from dbo.test1"
Invoke-Sqlcmd -Database AdventureWorks -Query "Insert INTO dbo.test1 values (1,2,3), (4,5,6)"
Invoke-Sqlcmd -Database AdventureWorks -Query "Select * from dbo.test1"
We can back up to the database level and remove the table.
cd .. Remove-Item dbo.Test1
Remove-Item dbo.Test1
This brief overview gives a flavour of what can be achieved using the PowerShell provider. As well as databases other objects within SQL Server are exposed by SMO including logins, jobs, backups and SQL Server 2008 policies. In order to make life easier the SQL Server team have added a PowerShell item to the context menu in SQL Server Management Studio. Just right click an object such as a database and select PowerShell to have the shell open at that point in the SMO hierarchy.
PowerShell is the automation and integration technology for Microsoft products and provides a very powerful addition to the DBA’s toolkit.