Deploying Web Site With PowerShell DSC part 3

In this last part of this series I’ll explain how to deploy database projects output to local database of node machine. It was the most difficult due to some errors present in the xDatabase resource. Actually I have a couple of Database Projects in my solution, the first one define the structure of the database needed by my application while the second one reference the first and installs only some test data with a Post Deploy Script. You can read about this technique in my previous post Manage Test Data in Visual Studio Database Project Sadly enough, the xDatabase resource of DSC is still very rough and grumpy.

I’ve found two distinct problems:

The first one is that DatabaseName is used as key property of the resource, this means that it is not possible to run two different DacPac on the same database because of duplicate key violation. This is usually a no-problem, because I could have deployed only the project with test data and since it reference the dacpac with the real structure of the site, both of them should deploy correctly. Unfortunately this does not happens, because you need to add some additional parameters deploy method, and xDatabase resource still not supports DacDeployOptions class. The fix was trivial, I changed the resource to use the name of the DacPac file as the key and everything just works.

The second problem is more critical and derives from usage of the DacService.Register method inside the script. After the first successful deploy, all the subsequent ones gave me errors. If you got errors during Start-DscConfiguration the output of the cmdlet, even in verbose mode, does not gives you details of real error that happened to target node where the configuration was run.  Usually what you get is a message telling: These errors are logged to the ETW channel called
Microsoft-Windows-DSC/Operational. Refer to this channel for more details

It is time to have a look to Event Viewer of the nodes where the failure occurred. Errors are located in Application And Service Logs / Microsoft / Windows / Desired State Configuration. Here is how I found the real error that xDatabase is raising on the target node.

image

Figure 1: Errors in event viewer of Target Node.

The error is in the update, DacServices.Deploy failed to update the database because it was registered as a Data Tier application and the Deploy command does not update its registration accordingly. This problem was easy to solve, because I need only to Specify RegisterDataTierApplication with a DacDeploymentOptions. I’ve added even this fix to the original xDatabase resource and I’ve added also more logging, so you are able to verify, when dsc runs, what DacServices class is really doing.

If you like I’ve posted my fix at this address: http://1drv.ms/1osn09U but remember that my fix are not thoroughly tested, and are not official Microsoft Correction in any way. So feel free to use them at your own risk. Clearly all these error will be fixed when the final version of xDatabase will be released (I remember you that these resources are pre-release, and this is the reason why they are prefixed with an x).

Now that xDatabase Resource works good, I can define a couple of resources to deploy my two dacpacs to target database.

xDatabase DeployDac 
{ 
    Ensure = "Present" 
    SqlServer = "." 
    SqlServerVersion = "2012" 
    DatabaseName = "TailspinToys" 
    Credentials = (New-Object System.Management.Automation.PSCredential("sa", (ConvertTo-SecureString "xxxxx" -AsPlainText -Force)))
    DacPacPath =  $AllNodes.SourceDir + "Tailspin.Schema.DacPac" 
    DacPacApplicationName = "Tailspin"
} 
    

xDatabase DeployDacTestData
{ 
    Ensure = "Present" 
    SqlServer = "." 
    SqlServerVersion = "2012" 
    DatabaseName = "TailspinToys" 
    Credentials = (New-Object System.Management.Automation.PSCredential("sa", (ConvertTo-SecureString "xxxxx" -AsPlainText -Force)))
    DacPacPath =  $AllNodes.SourceDir + "Tailspin.SchemaAndTestData.DacPac" 
    DacPacApplicationName = "Tailspin"
} 

Shame on me, I’m using explicit UserName and password again in DSC scripts, but actually if I omit Credentials to use integrated security, the xDatabase script fails with a NullReferenceException. Since this is a test server I accept to use clear text password until the xDatabase resource will not be fixed to support integrated authentication.

Here is the link to the full DSC script: http://1drv.ms/1osoIYZ. Have fun with DSC.

Gian Maria.

How to deploy a Web Site with Powershell Part 2

In the first part on “how to deploy a web site with Powershell DSC” I’ve explained the basic of a PowerShell DSC based script to install IIS and the required version of .NET Framework on target environment; now it is time to deploy a Web Site. In my scenario I want to use a port different from 80, because in test servers is a common practice installing multiple version of sites in different ports to distinguish between various deploy (Dev, Test, QA, etc). Here are the sequence of resources I use to deploy my site.

First of all I want the default Web Site to be stopped.

    xWebsite DefaultSite
    {
        Ensure = "Present"
        Name = "Default Web Site"
        PhysicalPath = "C:\inetpub\wwwroot"
        State = "Stopped"
        DependsOn = "[WindowsFeature]IIS"
    }

DSC is really powerful, because it can Install features, but also enforce that a specific resource is in a desired state. Since IIS installs a default Web Site, I want it to be in state “Stopped”. This step is not necessary since I’m going to use a different port for my application, but I do not like unnecessary site running in my test servers. Then it is time to copy files from drop location to a local folder on target node, a task easily accomplished by File Resource.

File TailspinSourceFiles
{
    Ensure = "Present"  # You can also set Ensure to "Absent"
    Type = "Directory“ # Default is “File”
    Recurse = $true
    SourcePath = $AllNodes.SourceDir + "_PublishedWebsites\Tailspin.Web" # This is a path that has web files
    DestinationPath = "C:\inetpub\dev\tailspintoys" # The path where we want to ensure the web files are present
}

The resource usage is straightforward, I simply want that DestinationPath contains the same sets of file of SourcePath recursively. Thanks to TFS Build I’m sure that TailspinToys compiled files are contained in a subfolder of the drop folder called _PublishedWebsites\Tailspin.Web. Remember that this script is executed in the target machine, so it needs to have access to the SourcePath directory.

Now it is time to fix the web.config file as needed. In resource pack there is xWebConfigKeyValue that can be used to change settings in AppSettings part of web config, but it does not allow changing connection strings. I want a solution that is capable of modifying any file in the target directory, because some team could use external XML files to store configuration and not web.config.

When there is no resource available for a DSC task and you can accomplish the task with few powershell script, the best solution is using the Script Resource, to specify the script you want to run in target node. Here is the solution.

#now change web config connection string
Script ChangeConnectionString 
{
    SetScript =
    {    
        $path = "C:\inetpub\dev\tailspintoys\Web.Config"
        $xml = Get-Content $path 

        $node = $xml.SelectSingleNode("//connectionStrings/add[@name='TailspinConnectionString']")
        $node.Attributes["connectionString"].Value = "Data Source=localhost;Initial Catalog=TailspinToys;User=sa;pwd=xxxx;Max Pool Size=1000"
        $xml.Save($path)
    }
    TestScript = 
    {
        return $false
    }
    GetScript = 
    {
        return @{
            GetScript = $GetScript
            SetScript = $SetScript
            TestScript = $TestScript
            Result = false
        }
    } 
}

Please do not shoot me for using sa in connection string :), it is a Super Bad practice, but actually my first goal is having a working script for a test server, that contains everything, so I do not mind if this site is connecting to local test Sql Server using sa. Remember to absolutely avoid doing this in production. The script resource contains three distinct part, SetScript, TestScript and GetScript and it is really similar to the structure used to create a DSC Resource.

The SetScript is the script that brings the node to desired state for this node resource. In this example I use some standard XPATH manipulation to load web.config, change a node and save it again. The TestScript is a piece of code that should return true if the condition is already met to avoid unnecessary run of SetScript. In my situation I could have tested if the value of the connection string is the desired one, but for the sake of brevity I simply return false, because the SetScript is idempotent and I do not mind running it every time so I always return false in TestScript part. The GetScript is some piece of standard code I’ve found in an example and I leaved it as it is.

Thanks to the Script DSC Resource I’m able to execute a custom script to the Node if there is no available resource to accomplish a simple task.

Then it is time of some file system security:

NTFSPermission AppDataSecurity
{
    Ensure = "Present"
    Account = "IIS AppPool\DefaultAppPool"
    Access = "Allow"
    Path = "C:\inetpub\dev\tailspintoys\app_data"
    Rights = "FullControl"
} 

This part is really important, because it permits you to specify worker process access level to various folders of the site. As a member of Operational Team, have you ever found yourself in the situation where you should deploy a web application and you have a zip with a bunch of files and no instructions. Then you unzip the file, create a IIS site ,point to the directory where you zipped the file, browse the file and get a Security Exception. Then you ask to some member of the dev team how to solve the problem and the solutions are.

  • Give to the worker role user Administrators permission
  • Give everyone full access on the whole site folder
  • Give write permission to C:\ to the worker role user.

Thanks to DSC dev team can specify exactly what permission the application needs. In this example I use Elmah with a local db, and it is clear that the application should be able to write under the app_data folder where I’ve located the database.

Thanks to DSC scripts, you can make explicit the various level of authentication that the application needs for various folders, and simplify the life of Ops team.

Finally you can use DSC to create the Web Site.

xWebsite TailspinToysSiteDev
{
    Ensure = "Present"
    Name = "Dev-Tailspintoys"
    PhysicalPath = "C:\inetpub\dev\tailspintoys"
    State = "Started"
    BindingInfo     = MSFT_xWebBindingInformation  
    {  
        Protocol              = "HTTP"  
        Port                  = 11000
    }  

    DependsOn = "[WindowsFeature]IIS"
}

This is a standard xWebsite resource you probably already know for various TechEd demo you can find in the internet. The only difference is the BindingInfo used to specify a different Binding, (in this example I want the site to bind to port 11000). Finally I need to be sure that a corresponding Firewall Rule open the 11000 port in the “domain” network, to make the site available to people in the domain.

xFirewall Firewall
{
    Name                  = "TailpinToys Dev"
    DisplayName           = "Firewall Rule for TailpinToys Dev"
    DisplayGroup          = "Tailspin"
    Ensure                = "Present"
    Access                = "Allow"
    State                 = "Enabled"
    Profile               = ("Domain")
    Direction             = "InBound"
    LocalPort             = ("11000")         
    Protocol              = "TCP"
    Description           = "Firewall Rule for TailpinToys Dev"  
}

This is another super interesting rule, because it is a Must Practice to write down all the port that an application is using to communicate with various networks. In this situation the example is quite trivial, if you install a Web Site and bind to port 11000 it is straightforward that a corresponding firewall rule should be created to make this site available to other machines. Now think to a desktop application that hosts some Self Hosted WCF Services. With xFirewall resource you are able to create all firewall rule you need and at the same time you are documenting with a clear syntax network requirements of your application. I strongly suggest you to give to test environment the same attention about security you would do to a production server, so always leave the firewall on on Test Server. (I ask you forgiveness for the sa in connection string :) that actually violates this principle, but I have no resource to create a corresponding user in Sql to use Integrated Authentication).

This concludes the second part of this tutorial. In the last part I’ll cover deploying structure and data to local Sql Server.

Stay tuned.

Gian Maria.

How to Deploy Web Site with PowerShell DSC

I do not want to create another tutorial on DSC and I suggest you reading some introductory articles like: Introducing PowerShell Desired State Configuration before reading this article. Since I’m pretty new with PowerShell and I’m starting experimenting with DSC I decided to start creating a script to deploy my favorite test application (TailspinToys :) ) on a single Windows 2012 R2 server using only DSC. This post aims to share my thought on the subject.

I was able to complete the script, even if I encountered some difficulties and I manage to automate almost everything, except the installation of Sql Server 2012 (I’m working on it). The goal is being able to deploy an application that uses a SQL server database written in Asp.Net 4.5 to a Windows Server with a fresh install, using only DSC Goodness.

First of all I warn you that most of the resources I needed to deploy my site are not available in basic distribution of PoweShell and should be downloaded from Microsoft. To download all the resources in a single package there is a single page in MSDN to download the entire DSC Resource Kit.

After you downloaded the resource kit you should care about a couple of important points, the first one is that these resources are not production ready and they are all experimental. This is the reason why all these resources starts with an x. So do not expect any official program to support them, if you have problem you should ask people in the forum and you will found solution. The other aspect is: if you, like me, appreciate the push model, you need to install all of these modules to all target servers. This violates in a certain way my requirement of being able to install in a clean server, because the server is not really “clean” if you need to have DSC resources deployed on it. This problem will be mitigated with WMF 5.0 that introduces the concept of PowerShellGet to automatically discover, install and update Powershell Modules, so it is really a no-problem.

Once everything is in place, I started creating the script, the first part is the standard one you can find in every PowerShell DSC related article, plus some import instructions to import all the DSC resources I want to use in the package.

Configuration TailspinToys
{
   
  Import-DscResource -Module xWebAdministration
  Import-DscResource -Module xNetworking
  Import-DscResource -Module xSqlPs
  Import-DscResource -Module xDatabase
  #http://www.vexasoft.com/blogs/powershell/9561687-powershell-4-desired-state-configuration-enforce-ntfs-permissions
  Import-DscResource -Module NTFSPermission

  Node $AllNodes.NodeName 
  { 
    

    #Install the IIS Role 
    WindowsFeature IIS 
    { 
      Ensure = “Present” 
      Name = “Web-Server” 
    } 

    # Required for SQL Server 
    WindowsFeature installdotNet35 
    {             
        Ensure = "Present" 
        Name = "Net-Framework-Core" 
        Source = "\\neuromancer\Share\Sources_sxs\?Win2012R2" 
    } 

    #Install ASP.NET 4.5 
    WindowsFeature ASP 
    { 
      Ensure = “Present” 
      Name = “Web-Asp-Net45” 
    } 

In the beginning of the script the Import-DscResource allow me to import the various resources I’ve installed, and NTFS Permission resource is taken from an article on VexaSoft site; many thanks to the author for authoring this module. That article is really useful because it shows how easy is create a resource for DSC in the situation where there is nothing already pre-made to obtain your purpose.

I use a configuration resource and the special name $AllNodes will contain the name of the single server I want to use for the installation. The above part of the scripts takes care of all of the prerequisites of my TailspinToys application. I’m installing .NET 3.5 because it is needed for Sql Server installation, but sadly enough I was not able to make the xSqlServerInstall works, to automatically install Sql Server (Actually it asks me to reboot and even rebooting the DSC scripts stops to run). I’ve decided to install Sql Server manually and wait for a better and more stable version of xSqlServerInstall. Then I request IIS and asp.net 4.5.

Running the above script with the right configuration data produces a mof file that can be used to actually configure the target. Here is the configuration I’,m using.

$ConfigurationData = @{
    AllNodes = @(
        @{
            NodeName="WebTest2"
            SourceDir = "\\neuromancer\Drops\TailspinToys_CD_WebTest1\TailspinToys_CD_WebTest1_20140213.1\"
            PSDscAllowPlainTextPassword=$true
            RebootNodeIfNeeded = $true
         }
   )
}

I need the name of the server and the source directory where I stored the distribution of my WebSite. In this example I’m using a standard Drop Folder of a TFS Build, so I have my binaries indexed with my symbol server. The creation of the mof file is simply triggered calling the new defined function TailspinToys passing the configuration above..

TailspinToys -ConfigurationData $ConfigurationData 

Now I have a mof file that contains everything I need to create the deploy, and I can push configuration to desired nodes with:

Start-DscConfiguration -Path .\TailspinToys -Wait -Verbose

This will start configuration, connect to all the nodes (in this example the single machine WebTest2) and “make it so”, moving the state of the nodes to desired state. The cool part of DSC is that you specify the state you desire on the target nodes, without taking care on how this state will be achieved, this is done by the various resources. Another interesting aspect is, if a resource is already in desired state, the Start-DscConfiguration will do nothing. When you run the above script the first time it needs a little bit time, because it will install IIS, but if IIS is already installed in target node, nothing happens.

With few lines of PowerShell I was able to install IIS and Asp.NET 4.5 plus .NET 3.5 to my machines.

In the next article I’ll deal on how to deploy the website bits.

Gian Maria.