Archive

Archive for the ‘PowerShell’ Category

Multi-database deployment with PowerShell and the Pivot Script Runner – Part 2

March 6, 2014 Leave a comment

In Part 1 of this article, we looked at using the PowerShell strengths to automate the process of updating several databases through the PivotRunner tool.

Now, we want to go further and create a PowerShell command, better known as a Cmdlet.

Build the Cmdlet

A Cmdlet can be built directly in a Powershell script, or through the .NET Framework. We need to inherit from System.Management.Automation.Cmdlet and define its naming attributes therefor.

By agreement, the name of a Cmdlet consists of a verb, followed by a dash and a name (e.g: Get-ChildItem andAdd-PSSnapIn):

using System.Management.Automation;

namespace CodeFluentEntitiesCmdlet
{
    [Cmdlet(VerbsData.Update, "CFEDatabase", SupportsShouldProcess = true, 
            ConfirmImpact = ConfirmImpact.High)]
    public class UpdateCFEDatabase : Cmdlet
    {
    }
}

Here, the Cmdlet’s name will be Update-CFEDatabase.

Use the following PowerShell command: Copy ([PSObject].Assembly.Location) C:\MyDllPath to find the System.Management.Automation library

SupportsShouldProcess and ConfirmImpact attributes allow the Cmdlet to use the PowerShell Requesting Confirmation feature.

The Cmdlet abstract class includes a fairly advanced command parameters engine to define and manage parameters:

[Parameter(Mandatory = true)]
public string ConnectionString { get; set; }

[Parameter(Mandatory = true)]
public string PivotFilePath { get; set; }

The Mandatory term is used to warn the command parameters engine of whether or not a parameter is required.

Cmdlet also exposes some methods which can be overriden. These pipeline methods allow the cmdlet to perform pre-processing operations, input processing operations, and post-processing operations.

Here, we’ll just override the ProcessRecord method:

protected override void ProcessRecord()
{
  // Process logic code
}

Then, we need to use the PivotRunner which is located in the CodeFluent.Runtime.Database assembly.

The tool takes the connection string and the pivot script producer output file as parameters:

using CodeFluent.Runtime;
using CodeFluent.Runtime.Database.Management.SqlServer;

private void UpdateDatabase()
{
    try
    {
        PivotRunner runner = new PivotRunner(PivotFilePath);

        runner.ConnectionString = ConnectionString;

        if (!runner.Database.Exists)
        {
            WriteObject("Error: The ConnectionString parameter does not lead to an existing database!");
            return;
        }
        runner.Run();
    }
    catch (Exception e)
    {
        WriteObject("An exception has been thrown during the update process: " + e.Message);
    }
}

Do not forget to reference CodeFluent.Runtime.dll and CodeFluent.Runtime.Database.dll!

Moreover, we can recover the PivotRunner output (internal logs) by providing an IServiceHost implementation:

public class CmdletLogger : IServiceHost
{
    private Cmdlet _cmdLet;

    public CmdletLogger(Cmdlet cmdlet)
    { 
        _cmdLet = cmdlet;
    }

    public void Log(object value)
    {
        _cmdLet.WriteObject(value);
    }
}

runner.Logger = new CmdletLogger(this);
runner.Run();

Powershell integration

The Cmdlet is now finished! 🙂

Now we’ll see how to call it from Powershell! Here, we have several options, but we shall see the PSSnapIn one.

The “Writing a Windows PowerShell Snap-in” article shows that a PSSnapIn is mostly a descriptive object which inherits from System.Configuration.Install.Installer and is used to register all the cmdlets and providers in an assembly.

So, let’s implement our Powershell snap-in:

using System.ComponentModel;
using System.Management.Automation;

namespace CodeFluentEntitiesCmdlet
{
    [RunInstaller(true)]
    public class CodeFluentEntitiesCmdletSnapin01 : PSSnapIn
    {
        public CodeFluentEntitiesCmdletSnapin01()
            : base() { }

        public override string Name
        {
            get { return ((object)this).GetType().Name; }
        }

        public override string Vendor
        {
            get { return "SoftFluent"; }
        }

        public override string VendorResource
        {
            get { return string.Format("{0},{1}", Name, Vendor); }
        }

        public override string Description
        {
            get { return "This is a PowerShell snap-in that includes the Update-CFEDatabase cmdlet."; }
        }

        public override string DescriptionResource
        {
            get { return string.Format("{0},{1}", Name, Description); }
        }
    }
}

Then, we build our solution which contains our Cmdlet and the PSSnapIn and finally register the built library thinks to the InstallUtil.exe (located in the installation folder of the .NET Framework):

Administrator rights are required.

Administrator rights are required.

By using the “Get-PSSnapIn –Registered” Powershell command, we can observe that our PSSnapIn is well registered. This component can now be used into your Powershell environment:

Get-PSSnapIn–Registered

The “Add-PSSnapIn” command enables us to use our Cmdlet into the current session of Powershell.

As result, we can update our previously built Powershell script:

param([string[]]$Hosts, [string]$PivotFilePath, [switch]$Confirm = $true)

Add-PSSnapin CodeFluentEntitiesCmdletSnapin01

if ($Hosts -eq $null -or [string]::IsNullOrWhiteSpace($PivotFilePath))
{
    Write-Error "Syntax: .\UpdateDatabase.ps1 -Hosts Host1[, Host2, ...] -PivotFilePath PivotFilePath"
    break
}

[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | out-null

Write-Host "-========- Script started -========-"

$Hosts | foreach {
    $srv = new-object ('Microsoft.SqlServer.Management.Smo.Server') $_

    $online_databases = $srv.Databases | where { $_.Status -eq 1 -and $_.Name.StartsWith("PivotTest_") }
    
    if ($online_databases.Count -eq 0)
    {
        Write-Error "No database found"
        break
    }

    Write-Host "Database list:"
    $online_databases | foreach { Write-Host $_.Name }

    [string]$baseConnectionString = "$($srv.ConnectionContext.ConnectionString);database="
    $online_databases | foreach {
        Update-CFDatabase -ConnectionString "$($baseConnectionString)$($_.Name)" -PivotFilePath $PivotFilePath -Confirm:$Confirm
    }
}

Write-Host "-========-  Script ended  -========-"

We can now simply deploy all changes we’ve recently made on our databases thanks to the Cmdlet and PivotRunner components.

The source code is available for download.

Happy PowerShelling !

The R&D team

Multi-database deployment with PowerShell and the Pivot Script Runner – Part 1

February 13, 2014 2 comments

We are working on a solution designed with CodeFluent Entities, which stores and manages data within a SQL Server database created by the SQL Server Producer.
Sometime in the past, we deployed this solution on many servers and, now, we want to keep them all up to date.
Recently, we have made some important changes and we want to deploy them on our many databases.

How to set up and automate the process of updating a range of databases while preserving their content?

Pivot Script Producer

To answer this question, we have developed a new producer called “The Pivot Script Producer”. It provides the opportunity for generating one or more XML files which are a database snapshot of the current CodeFluent Entities project model.

Pivot Runner

These files, generated by the pivot script producer, are intended to be consumed by the PivotRunner tool of the CodeFluent.Runtime.Database library.
Using a connection string, it updates the targeted database from the files we have previously sent to him.

The New SQL Server Pivot Script producer article shows that we can directly use the PivotRunner API from the library. But, even easier, we can just call one of the provided programs: CodeFluent.Runtime.Database.Client.exe or CodeFluent.Runtime.Database.Client4.exe, located in the CodeFluent Entities installation folder.

At this stage, we can very easily and quickly update one database. But we still want to apply this process on several databases!

PowerShell Script

Let’s use the PowerShell strengths ! 🙂

Powershell is a scripting language developed by Microsoft and default running on any Windows system since Windows Seven. With a fully object-oriented logic and a very close relationship with the .NET Framework, it has become an essential and very simple and useful tool. And that’s why Powershell is so cool!

Thus, we could easily imagine a script that takes a list of servers as first parameter, and the generated files path as the second one to select and update the targeted databases (here, only the online databases whose name starts with “PivotTest_”).

param([string[]]$Hosts, [string]$PivotFilePath)

if ($Hosts -eq $null -or [string]::IsNullOrWhiteSpace($PivotFilePath))
{
    Write-Error "Syntax: .\UpdateDatabases.ps1 -Hosts Host1[, Host2, ...] -PivotFilePath PivotFilePath"
    break
}

[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | out-null

Write-Host "-========- Script started -========-"

$Hosts | foreach {
    $srv = new-object ('Microsoft.SqlServer.Management.Smo.Server') $_

    $online_databases = $srv.Databases | where { $_.Status -eq 1 -and $_.Name.StartsWith("PivotTest_") }
    
    if ($online_databases.Count -eq 0)
    {
        Write-Error "No database found"
        break
    }

    Write-Host "Database list:"
    $online_databases | foreach { Write-Host $_.Name }

    [string]$baseConnectionString = "$($srv.ConnectionContext.ConnectionString);database="
    $online_databases | foreach {
        & “CodeFluent.Runtime.Database.Client.exe” “runpivot” $PivotFilePath "$($baseConnectionString)$($_.Name)"
}
}

Write-Host "-========-  Script ended  -========-"

The script above shows us how easily some .NET Framework features can be used. In particular the Microsoft.SqlServer.Management.Smo namespace that provides an intuitive way of SQL Server instances manipulation.

We could just call one of the CodeFluent Runtime Database programs described above, but the idea of using directly the PivotRunner through a custom PowerShell command is much more attractive!

Indeed, Powershell gives us that opportunity. These customs commands are called “Cmdlets” and can be built under C#.NET, as we will see later in the second part of this article 🙂

Happy deploying

The R&D team.