SQL

This post is part of the Second Wednesday Demo Session, Click here for more info about additional demo posts.

It’s possible to manage SQL Server using Windows PowerShell. First let’s see how to create a New Database.
In these examples we are using Microsoft.SqlServer.Smo so step one is adding the class to our current session.


PS > Add-Type "Microsoft.SqlServer.Smo, Version=10.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91"

Creating a new database using PowerShell requires three commands, first we connect to the server, then we create a new instance of Microsoft.SqlServer.Management.Smo.Database and finally we use
the Create() method to create the new database.


PS > $Server = New-Object Microsoft.SqlServer.Management.Smo.Server("SQL01")
PS > $db = New-Object Microsoft.SqlServer.Management.Smo.Database("SQL01", "TestDB")
PS > $db.Create()

It’s possible to configure additional settings in the database such as growth, logfile etc. If you want to find out more I would recommend Richard Siddaways book: PowerShell in Practise.

Next let’s create a Simple Table in our Database.


PS > $Server = New-Object Microsoft.SqlServer.Management.Smo.Server("SQL01")

PS > $script = @"
>> SET ANSI_NULLS On
>> SET QUOTED_IDENTIFIER ON
>> CREATE TABLE [dbo].[Computers](
>> [ComputerID] [int]IDENTITY(1,1) NOT NULL,
>> [Computer] [nvarchar](50) NOT NULL,
>> [Description] [nvarchar](50) NOT NULL)
>> "@
PS > $db = $server.Databases["TestDB"]
PS > $db.ExecuteNonQuery($script)

In the example above we connect to the server using Microsoft.SqlServer.Management.Smo.Server. Next we store some SQL Syntax in the variable $script. Then we store the database in a variable and
use the ExecuteNonQuery() method to create the Table.

Now let’s add some information to our table. First let’s add a single computer to our table. When adding information to a table in SQL we can use System.Data.SQLClient.SQLConnection and
System.Data.SQLClient.SQLCommand as demonstrated in the function below. We use a parameterized query to avoid possible injections.


function New-SQLComputer ([string]$ComputerName, [string]$Description) {
  # Connection
  $Connection = New-Object System.Data.SQLClient.SQLConnection

  # Add ConnectionString
  $Connection.ConnectionString = "server=SQL01;database=TestDB;trusted_connection=true;"
  # Open Connection
  $Connection.Open()

  # SQL Command
  $Command = New-Object System.Data.SQLClient.SQLCommand
  $Command.Connection = $Connection

  $SQLParameter1 = New-Object System.Data.SqlClient.SqlParameter("@ComputerName", $ComputerName)
  $SQLParameter2 = New-Object System.Data.SqlClient.SqlParameter("@Description", $Description)
  [void]$command.Parameters.Add($SQLParameter1)
  [void]$command.Parameters.Add($SQLParameter2)

  # Add T-SQL Query
  $Command.CommandText = "INSERT Computers VALUES (@ComputerName,@Description)"

  # Execute
  $Command.ExecuteNonQuery()

  # Close Connection
  $Connection.Close()
}

Now we can use the function to add a new Row in the Table. The example below demonstrates how to add a single Row.


PS > New-SQLComputer -ComputerName Test001 -Description "PowerShell Demo"

In the example above we use INSERT to add a new row in the table. let’s add alot of rows.

Here’s an example how you can add multiple rows in a table using a for loop.


for($i = 1;$i -le 200;$i++) {
  $ComputerName = "DT{0:D3}" -f $i
  $Description = "Added Using PowerShell"

  New-SQLComputer -ComputerName $ComputerName -Description $Description
}

Rating 3.00 out of 5
[?]

SharePoint 2010

This post is part of the Second Wednesday Demo Session, Click here for more info about additional demo posts.

In a previous post we used Windows PowerShell to Create a Database, add a table and populate the table with alot of entries.

In the last part of this Demo we’ll use SharePoint 2010 to populate a List in SharePoint based on a Table in SQL and finally Script up alot of Computers based on a SharePoint List.
First let’s take a look at the SharePoint List.

The List contains three Columns that we will use: Title, Description and Create. Create in this example is a Yes/No column.

In a previous post we populated a Table in SQL with Computers. Let’s take a look at how we can read information from that list. The function below demonstrates how we can read data from a table.


function Get-SQLComputer {

  $Connection = New-Object System.Data.SqlClient.SqlConnection
  $Connection.ConnectionString = "server=SQL01;database=TestDB;trusted_connection=true;"

  # open Connection
  $Connection.Open()
  $Command = New-Object System.Data.SQLClient.SQLCommand
  $Command.Connection = $Connection
  # Add Query
  $Command.CommandText = "SELECT * FROM Computers"
  # read from table
  $Reader = $Command.ExecuteReader()
  $Counter = $Reader.FieldCount
  # itterate each row
  while ($Reader.Read()) {
      $SQLObject = @{}
      for ($i = 0; $i -lt $Counter; $i++) {
      $SQLObject.Add(
        $Reader.GetName($i),
        $Reader.GetValue($i)
      );
    }
    # return hashTable
    $SQLObject
  }
  # Close Connection
  $Connection.Close()
}

The function above returns the rows in the table. Now let’s add them to our custom SHarePoint list.

Note that the function above doesn’t require any input. If you write a function that takes input when performing a query you should consider using parameterized queries to avoid possible injections. here’s a short example:


function Get-SQL([string]$ComputerName) {

  $Connection = New-Object System.Data.SqlClient.SqlConnection
  $Connection.ConnectionString = "server=SQL01;database=TestDB;trusted_connection=true;"

  $Connection.Open()
  $Command = New-Object System.Data.SQLClient.SQLCommand
  $Command.Connection = $Connection

  $Command.CommandText = "SELECT * FROM Computers WHERE Computer = @ComputerName"

  # These two lines are added to avoid injections
  $SQLParameter = New-Object System.Data.SqlClient.SqlParameter("@ComputerName", $ComputerName)
  [void]$command.Parameters.Add($SQLParameter)

  $Reader = $Command.ExecuteReader()
  $Counter = $Reader.FieldCount

  while ($Reader.Read()) {
      $SQLObject = @{}
      for ($i = 0; $i -lt $Counter; $i++) {
      $SQLObject.Add(
        $Reader.GetName($i),
        $Reader.GetValue($i)
      );
    }

    $SQLObject
  }

  $Connection.Close()
}

First we use Add-PSSNapin to add the SharePoint 2010 CmdLets.


PS > Add-PSSNapin Microsoft.SharePoint.PowerShell

If your not running in sta mode, set the threadoption to “ReuseThread”.


PS > $host.Runspace.ThreadOptions = "ReuseThread"

Next we use Get-SPWeb to return a specific Site.


PS > $spWeb = Get-SPWeb http://SP01

Now we get the “Computer” list using the GetList() method.


PS > $spList = $spWeb.GetList("/Lists/Computers")

The spList has a method, AddItem(), which we can use to add a new Item in the SharePoint List. the example below demonstartes how we use the Get-SQLComputer function
to add the computers from the SQL table to a list in SharePoint 2010.


PS > Get-SQLComputer | ForEach {
>> $item = $spList.AddItem()
>> $item["Title"] = $($_.Computer).TrimEnd()
>> $item["Description"] = $($_.Description).TrimEnd()
>> $item.Update()
>> }

Finally we dispose of the spWeb object.


PS > $spWeb.Dispose()

If we take a look at the list now, the Computers are added.

Finally, let’s create some of the computers in Active-Directory. The SharePoint List had a Yes/No Column named Create. Let’s only create computers where the field is set to Yes.

In this example I’ve checked the Create filed for: DT001, DT003 and DT005. Let’s see how we can retrieve does specific items from SharePoint 2010 wqithout retrieving the whole ListItem Collection.
First we get the specific list.


PS > $spWeb = Get-SPWeb http://SP01
PS > $spList = $spWeb.GetList("/Lists/Computers")

The spList contains a property, ItemCount, that show us how many ListItems that are in the list.


PS > $spList.ItemCount

202

The spList also contains a property, Items, that returns all items in the list. Note that this can consume alot of unnecesary memory if your working with large lists so a better way of retrieving listitems is by using a CAML query.
In this example we only want listItems where Create equals Yes. Here’s an example on how to write such a query.


PS > $spQuery = New-Object Microsoft.SharePoint.SPQuery;
PS > $query = "<Where><Eq><FieldRef Name='Create' /><Value Type='Boolean'>1</Value></Eq></Where>"
PS > $spQuery.Query = $query

An easy way of getting your CAML queries correct is by using a CAML query builder.

Now we can Get the specific listItems using the GetItems() method and the spQuery object as input.


PS > $listItems = $spList.GetItems($spQuery)

If we count the number of listItems in the ListItem Collection we’ll see that it only contains 3 items.


PS > $listItems.Count

3

We can use the Select-Object cmdlet to Select the properties that we want to work with.


PS > $listItems | Select-Object @{Name="Name";Expression={$_.Title}},
>> @{Name="Description";Expression={$_["Description"]}}

Name                  Description
----                  -----------
DT001                 Added Using PowerShell
DT003                 Added Using PowerShell
DT005                 Added Using PowerShell

Finally, let’s go ahead and create the computers in Active Directory. In this example well start a Rmote background job that does the work for us.


PS > $computers = $listItems | Select-Object @{Name="Name";Expression={$_.Title}},
>> @{Name="Description";Expression={$_["Description"]}}
>> Invoke-Command -ComputerName DC01 -ScriptBlock {
>>   Import-Module ActiveDirectory
>>   $args | Foreach {
>>     New-ADComputer -Name $($_.Name) -Description $($_.Description) -Path "OU=LabCenter,DC=PowerShell,DC=nu"
>>   }
>> } -ArgumentList $computers -AsJob
>>

Id              Name            State      HasMoreData     Location             Command
--              ----            -----      -----------     --------             -------
7               Job7            Running    True            dc01                  ...

When the job completes the Computers are created in Active Directory

Rating 4.00 out of 5
[?]

Another Handy SQL function in PowerShell

The Get-SQL function retrieved data from SQL and presented it in a HashTable Array.

Let’s create a similar function that we can use when updating, inserting or deleting data from an SQL Table.


function Set-SQL ([string]$Query,[string]$ConnectionString) {
  $Connection = New-Object System.Data.SQLClient.SQLConnection

  $Connection.ConnectionString = $ConnString
  $Connection.Open()

  $Command = New-Object System.Data.SQLClient.SQLCommand
  $Command.Connection = $Connection
  $Command.CommandText = $Query

  return $Reader = $Command.ExecuteNonQuery()

  $Connection.Close()
}

Examples on Running the Function


PS > Set-SQL -Query "INSERT INTO Region (RegionID,RegionDescription) VALUES ('5','Even More North')" `
-ConnString "server=Server;database=Northwind;trusted_connection=true;"

PS > Set-SQL "DELETE * FROM Region WHERE RegionID = '5'" `
-ConnString "server=Server;database=Northwind;trusted_connection=true;"

Again, if you are working with user input or any input that you do not control 100% you should use parameterized queries instead.
Here’s an example on how you can secure your function.


function Set-SQL ([int]$Id, [string]$Description, [string]$ConnectionString) {
  $Connection = New-Object System.Data.SQLClient.SQLConnection

  $Connection.ConnectionString = $ConnectionString
  $Connection.Open()

  $Command = New-Object System.Data.SQLClient.SQLCommand
  $Command.Connection = $Connection
  $Command.CommandText = "INSERT INTO Region (RegionID,RegionDescription) VALUES (@RegionID,@RegionDescription)"

  $SQLParameter1 = New-Object System.Data.SqlClient.SqlParameter("@RegionID", $Id)
  $SQLParameter2 = New-Object System.Data.SqlClient.SqlParameter("@RegionDescription", $Description)
  [void]$command.Parameters.Add($SQLParameter1)
  [void]$command.Parameters.Add($SQLParameter2)

  return $Reader = $Command.ExecuteNonQuery()

  $Connection.Close()
}

Rating 3.00 out of 5
[?]

Handy SQL Function in PowerShell

In a previous post I described a little about PowerShell and SQL, how to connect and how to handle different queries using PowerShell.

Now let’s put it all togheter and create a more useful function.


function Get-SQL ([string]$Query,[string]$ConnectionString) {
  $Connection = New-Object System.Data.SQLClient.SQLConnection

  $Connection.ConnectionString = $ConnectionString
  $Connection.Open()

  $Command = New-Object System.Data.SQLClient.SQLCommand
  $Command.Connection = $Connection
  $Command.CommandText = $Query

  $Reader = $Command.ExecuteReader()
  $Counter = $Reader.FieldCount
  while ($Reader.Read()) {
    $SQLObject = @{}
      for ($i = 0; $i -lt $Counter; $i++) {
        $SQLObject.Add(
          $Reader.GetName($i),
          $Reader.GetValue($i)
        );
      }
    $SQLObject
  }
  $Connection.Close()
}

Examples on Running the Function:


PS > Get-SQL -Query "SELECT * FROM Products" `
>> -ConnectionString "server=SQL01;database=DbName;trusted_connection=true;"

Note that if you are planning on taking user input or any input that you don’t control 100% you should use parameterized queries instead.
Consider the following example:


PS > Get-SQL -Query "SELECT * FROM Table1; DELETE From Table WHERE Name = 'Something'" `
>> -ConnectionString "servr=DC01;database=TestDB;trusted_connection=true;"

In the example above all rows in the Table are returned, but as you can see, one row is also deleted.
To avoid this you should use parameterized queries instead. Here’s an example on how you can create a function that uses parameterized queries.


function Get-SQL ([string]$Name,[string]$ConnectionString) {
  $Connection = New-Object System.Data.SQLClient.SQLConnection

  $Connection.ConnectionString = $ConnectionString
  $Connection.Open()

  $Command = New-Object System.Data.SQLClient.SQLCommand
  $Command.Connection = $Connection
  $Command.CommandText = "SELECT * From Computers WHERE Computer = @Name"

  # 2 Additional lines
  $SQLParameter = New-Object System.Data.SqlClient.SqlParameter("@Name", $Name)
  [void]$command.Parameters.Add($SQLParameter)

  $Reader = $Command.ExecuteReader()
  $Counter = $Reader.FieldCount
  while ($Reader.Read()) {
    $SQLObject = @{}
      for ($i = 0; $i -lt $Counter; $i++) {
        $SQLObject.Add(
          $Reader.GetName($i),
          $Reader.GetValue($i)
        );
      }
    $SQLObject
  }
  $Connection.Close()
}

Rating 3.00 out of 5
[?]

SQL through PowerShell

Working with Databases using PowerShell can be a little tricky from time to time. In this post we’ll take a look at how to work with database tables using .NET and we’ll also check out some CmdLets available. Let’s start connecting to our SQL database using the SQLConnection object:


PS > $Connection = New-Object System.Data.SQLClient.SQLConnection
PS > $Connection


StatisticsEnabled                : False
ConnectionString                 :
ConnectionTimeout                : 15
Database                         :
DataSource                       :
PacketSize                       : 8000
ServerVersion                    :
WorkstationId                    : Server
FireInfoMessageEventOnUserErrors : False
State                            : Closed
Site                             :
Container                        :

Next, we have to set the ConnectionString, this defines which DataBase that we want to connect to.
Note that the authentication method that is used in this example is integrated authentication. If you use non-integrated authentiacation, you can add “userid=username;password=password;” to the connection string.

Note that the database we will use in these examples is Northwind which is a sample database from Microsoft that you can download for free at Microsoft.com


PS > $Connection.ConnectionString = 
"server=Server;database=Northwind;trusted_connection=true;"
PS > $Connection.ConnectionString


server=Server;database=Northwind;trusted_connection=true;

And when all is set, we open the Connection.


PS > $Connection.Open()

When you are finished using your database, you can close it with the close() method.


PS > $Connection.Close()

Now that we’ve opened a connection to our database, we can start with a couple of queries. The four basic queries are:

  • SELECT
  • INSERT
  • UPDATE
  • DELETE

When executing queries, use the SQLCommand Object. Note that if you use INSERT, UPDATE or DELETE, you won’t get a value returned so you should use the ExecuteNonQuery() method. In case of SELECT, you should use the ExecuteReader() method. Let’s start looking at the SELECT query:

Northwind contains a table called Products. So if we want to see all Columns in Products we would use “SELECT * from Products” to retrieve all columns and rows:

Note. In order to retrieve the data from our database, we have to use the ExecuteReader() method and
loop through it. Column Names are retrieved through .GetName() and values are retrieved through .GetValue()


PS > $Command = New-Object System.Data.SQLClient.SQLCommand
PS > $Command.Connection = $Connection
PS > $Command.CommandText = "SELECT * FROM Products"

PS > $Reader = $Command.ExecuteReader()
PS > $Counter = $Reader.FieldCount
while ($Reader.Read()) {
	for ($i = 0; $i -lt $Counter; $i++) {
		@{ $Reader.GetName($i) = $Reader.GetValue($i); }
	}
}

Name                           Value
----                           -----
ProductID                      1
ProductName                    Chai
SupplierID                     1
CategoryID                     1
QuantityPerUnit                10 boxes x 20 bags
UnitPrice                      18,0000
UnitsInStock                   39
UnitsOnOrder                   0
ReorderLevel                   10
Discontinued                   False
ProductID                      2
ProductName                    Chang
SupplierID                     1
CategoryID                     1
QuantityPerUnit                24 - 12 oz bottles
UnitPrice                      19,0000
UnitsInStock                   17
UnitsOnOrder                   40
ReorderLevel                   25
Discontinued                   False
ProductID                      3
ProductName                    Aniseed Syrup
SupplierID                     1
CategoryID                     2
QuantityPerUnit                12 - 550 ml bottles
UnitPrice                      10,0000

If we want a specific Row in the table, we can specify it in our SELECT query.


PS > $Command = New-Object System.Data.SQLClient.SQLCommand
PS > $Command.Connection = $Connection
PS > $Command.CommandText = "SELECT * FROM Products where ProductName = 'Röd kaviar'"

PS > $Reader = $Command.ExecuteReader()
PS > $Counter = $Reader.FieldCount
PS > while ($Reader.Read()) {
	for ($i = 0; $i -lt $Counter; $i++) {
		@{ $Reader.GetName($i) = $Reader.GetValue($i); }
	}
}

Name                           Value
----                           -----
ProductID                      73
ProductName                    Röd Kaviar
SupplierID                     17
CategoryID                     8
QuantityPerUnit                24 - 150 g jars
UnitPrice                      15,0000
UnitsInStock                   101
UnitsOnOrder                   0
ReorderLevel                   5
Discontinued                   False

And if we want to check out another table, all we have to do is change the SELECT query as shown below.


PS > $Command = New-Object System.Data.SQLClient.SQLCommand
PS > $Command.Connection = $Connection
PS > $Command.CommandText = "SELECT * FROM Region"
PS > $Reader = $Command.ExecuteReader()
PS > $Counter = $Reader.FieldCount
PS > while ($Reader.Read()) {
	for ($i = 0; $i -lt $Counter; $i++) {
		@{ $Reader.GetName($i) = $Reader.GetValue($i); }
	}
}


Name                           Value
----                           -----
RegionID                       1
RegionDescription              Eastern
RegionID                       2
RegionDescription              Western
RegionID                       3
RegionDescription              Northern
RegionID                       4
RegionDescription              Southern

Let’s check out the INSERT query. INSERT is used when you want to add new data to a table. First specify the Columns that you want to INSERT into and the specify the Values.

Let’s play a little with the Region table.


PS > $Command = New-Object System.Data.SQLClient.SQLCommand
PS > $Command.Connection = $Connection
PS > $Command.CommandText = 
	"INSERT INTO Region (RegionID,RegionDescription) VALUES ('5','Even More North')"
PS > $Command.ExecuteNonQuery()

1

The return value is 1 since the row was affected.

Let’s check that the row really was affected by using a SELECT query:


PS > $Command = New-Object System.Data.SQLClient.SQLCommand
PS > $Command.Connection = $Connection
PS > $Command.CommandText = "SELECT * FROM Region where RegionID = '5'"
PS > $Reader = $Command.ExecuteReader()
PS > $Counter = $Reader.FieldCount
PS > while ($Reader.Read()) {
	for ($i = 0; $i -lt $Counter; $i++) {
		@{ $Reader.GetName($i) = $Reader.GetValue($i); }
	}
}


Name                           Value
----                           -----
RegionID                       5
RegionDescription              Even More North

The UPDATE query modifies existing data. It’s often used with a WHERE clause to limit the range of affect to the specified rows. Let’s UPDATE the row that we INSERTED above.


PS > $Command = New-Object System.Data.SQLClient.SQLCommand
PS > $Command.Connection = $Connection
PS > $Command.CommandText = 
"UPDATE Region set RegionDescription = 'Go West' WHERE RegionID = '5'"

PS > $Command.ExecuteNonQuery()

1

And again, we check that our row was affected.


PS > $Command = New-Object System.Data.SQLClient.SQLCommand
PS > $Command.Connection = $Connection
PS > $Command.CommandText = "SELECT * FROM Region where RegionID = '5'"
PS > $Reader = $Command.ExecuteReader()
PS > $Counter = $Reader.FieldCount
PS > while ($Reader.Read()) {
	for ($i = 0; $i -lt $Counter; $i++) {
		@{ $Reader.GetName($i) = $Reader.GetValue($i); }
	}
}


Name                           Value
----                           -----
RegionID                       5
RegionDescription              Go West

DELETE will remove data from the database.


PS > $Command = New-Object System.Data.SQLClient.SQLCommand
PS > $Command.Connection = $Connection
PS > $Command.CommandText = "DELETE * FROM Region WHERE RegionID = '5'"
PS > $Command.ExecuteNonQuery()

1

And, finally, we check that the data really was deleted.


PS > $Command = New-Object System.Data.SQLClient.SQLCommand
PS > $Command.Connection = $Connection
PS > $Command.CommandText = "SELECT * FROM Region"
PS > $Reader = $Command.ExecuteReader()
PS > $Counter = $Reader.FieldCount
PS > while ($Reader.Read()) {
	for ($i = 0; $i -lt $Counter; $i++) {
		@{ $Reader.GetName($i) = $Reader.GetValue($i); }
	}
}


Name                           Value
----                           -----
RegionID                       1
RegionDescription              Eastern
RegionID                       2
RegionDescription              Western
RegionID                       3
RegionDescription              Northern
RegionID                       4
RegionDescription              Southern

Below is the Code used in this post.


$Connection = New-Object System.Data.SQLClient.SQLConnection
$Connection.ConnectionString = 
  "server=Server;database=Northwind;trusted_connection=true;"
$Connection.Open()

$Query = "SELECT * FROM Products

$Command = New-Object System.Data.SQLClient.SQLCommand
$Command.Connection = $Connection
$Command.CommandText = $Query

$Reader = $Command.ExecuteReader()
$Counter = $Reader.FieldCount
while ($Reader.Read()) {
	for ($i = 0; $i -lt $Counter; $i++) {
		@{ $Reader.GetName($i) = $Reader.GetValue($i); }
	}
}

SQL Server 2008 R2 includes two snapins, SqlServerCmdletSnapin100 and SqlServerProviderSnapin100.
The SqlServerCmdletSnapin100 adds the Invoke-SQLCmd cmdLet. So instead of typing the code in the examples above
we can simply Add the snapin to our current session and use the CmdLet.


PS > Add-PSSNapin SqlServerCmdletSnapin100
PS > Invoke-Sqlcmd -Database NorthWind -Query "SELECT * FROM Products" -ServerInstance Server01

The SqlServerProviderSnapin100 adds a new PSDrive that when working with SQL.


PS > Add-PSSnapin SqlServerProviderSnapin100
PS > Get-PSDrive

Name           Used (GB)     Free (GB) Provider      Root
----           ---------     --------- --------      ----
A                                      FileSystem    A:\
Alias                                  Alias
C                  15,07         84,83 FileSystem    C:\
cert                                   Certificate   \
D                                      FileSystem    D:\
Env                                    Environment
Function                               Function
HKCU                                   Registry      HKEY_CURRENT_USER
HKLM                                   Registry      HKEY_LOCAL_MACHINE
SQLSERVER                              SqlServer     SQLSERVER:\
Variable                               Variable
WSMan                                  WSMan

After the SnapIn is added we can use the Provider to access SQL Server.


PS > cd SQLServer:
PS SQLSERVER:\> dir

Rating 3.67 out of 5
[?]