Thursday, September 13, 2012

The Story of Automated Deployment


I’m going to tell you the story about automating deployment of large and complex Sitecore project in a real-world.

It all began with a 15-pages document describing website update procedure. There was a huge number of tweaks to website configuration files, machine.config file.

Some were really quick, the other ones could take up to half an hour, for example, creating DB backups, restoring them according to the manual, etc.

And the chance of mistake was so high, that it was rather expected to forget something like uncommenting section in one of three different .include files.

Overall, it could easily take 5-6 hours to deploy the solution to production, without even brief testing.
Taking it all into account, the goal to automate it seemed to be impossible. How do we backup and restore right databases into the proper locations, set attributes, uncomment web.config sections, automate IIS?
The answer was so close all the time – PowerShell.

By the end of the story, you’ll see how we’ve managed to reduce documentation size to just one page (saying “Click button X”, “Click Button Y”), completely remove manual steps and reduce total deployment time (to both Content Management and multiple Content Delivery servers) to something about 1 hour, with no website downtime.

So, here’s the brief process overview. From build server to Testing and Production environment - without delays and completely automatically


During the release, new site (let’s call it Staging) is created and configured, while the Production site is running. If everything works well, Staging is simply switched to Production (in just a second), so we get no downtime at all. The other side of this solution is that Content Editors cannot edit content after we’ve created backups of Production, and not yet released Staging.

PowerShell can automate nearly anything. Not your daily job… yet, but all the stuff you can express in documentation.

Deployment process can be split into the different phases:
  1. Extract files, prepare folders structure, etc.
  2. Update configuration files, setup security
  3. Backup old databases, create new ones and restore from backups
  4. Create Application Pool, Website in IIS and configure their values
  5. Start the website, install item update packages, rebuild indexes, etc.
  6. Copy data to one or more Content Delivery servers
  7. Switch the Staging and Production websites
Below, I’ll show the snippets for each of these steps. Since every project is different in terms of deployment processes, this is the easiest way to understand it. If you have any questions or have a doubts you can automate something – I’ll be glad to help, just leave a comment to this blog post or use another way to contact me (Twitter, LinkedIn, etc.).

Extract files

Fairly simple, first, you may also need to download the files for build server:
  1. $ client = new-object System.Net.WebClient  
  2. $ client.DownloadFile($url$location)    
Then use the following code to unzip files from $archiveFile (like “C:\BuildOutput.zip”) to $targetFolder:
  1. $shell_app=new-object -com shell.application   
  2. $zip_file = $shell_app.namespace($archiveFile)   
  3. $destination = $shell_app.namespace($targetFolder)  
  4. $destination.Copyhere($zip_file.items(), 0x14)  

Configuration files

You may need to update web.config, here’s how to set Data folder:
  1. $webConfigPath = $webConfigTemplatePath.Substring(0, $webConfigTemplatePath.LastIndexOf('.'))  
  2. $webConfig = [xml](get-content $webConfigPath)  
  3. $webConfig.configuration.SelectSingleNode("sitecore/sc.variable[@name='dataFolder']").SetAttribute("value"$dataFolder)      
  4. $webConfig.Save($webConfigPath)  
You can also easily change Sitecore settings in web.config or .include files:
  1. $sampleConfigPath = "$webroot\website\App_Config\Include\Sitecore.Sample.config"  
  2. $sampleConfig = [xml](get-content $sampleConfigPath)  
  3. $sampleConfig.configuration.sitecore.SelectSingleNode("settings/setting[@name=SampleSetting]").SetAttribute("value""true")  
  4. $sampleConfig.Save($sampleConfigPath)  

Backup Databases

No Sitecore-specific tasks here, I’ll just show how to back up the database
  1. $dbBackup = new-Object ("Microsoft.SqlServer.Management.Smo.Backup")  
  2. $dbRestore = new-object ("Microsoft.SqlServer.Management.Smo.Restore")  
  3.   
  4. $dbBackup.Database = $d.Name  
  5.   
  6. $backupFile = $backupFolder + "\" + $dbName + ".bak" 
  7. Write-Host "Backup file:" $backupFile 
  8.  
  9. $dbBackup.Devices.AddDevice($backupFile, "File") 
  10.  
  11. $dbBackup.Action="Database"  
  12. $dbBackup.Initialize = $TRUE  
  13. $dbBackup.PercentCompleteNotification = 10  
  14. $dbBackup.SqlBackup($server)    

Create Application Pool, Website, etc.

You can create new Application Pool like this
  1. $serverManager = New-Object Microsoft.Web.Administration.ServerManager;  
  2. $appPool = $serverManager.ApplicationPools.Add($sitename);  
  3. $appPool.ManagedRuntimeVersion = “v4.0”  
  4. $appPool.ProcessModel.username = [string]($user)  
  5. $appPool.ProcessModel.password = [string]($password)  
  6. $appPool.ProcessModel.identityType = "SpecificUser"  
  7. $appPool.ProcessModel.IdleTimeout = [TimeSpan] "0.00:00:00"  

Then create a site
  1. $webSite = $serverManager.Sites.Add($sitename"http"":80:www.mysite.net "$webroot + "\website");  
  2. $webSite.Applications[0].ApplicationPoolName = $sitename;  

And commit the changes
  1. $serverManager.CommitChanges();  

Install package, rebuild indexes, and the stuff

The easiest way to implement it is to write some utility pages and just execute web request from PowerShell.
  1. $pageUrl = "http://mysite.net/Util/RebuildIndex.aspx”  
  2. $pageRequest = [system.net.WebRequest]::Create($pageUrl)  
  3. $pageRequest.Timeout = 1200000  
  4. $pageRequest.GetResponse()  

Switch the Staging site to Production
First you may need to stop both sites
  1. Stop-WebSite -Name $liveSiteName  
  2. Stop-WebSite -Name $stagingSiteName  
Then set correct binding in IIS for the new Production site
  1. New-WebBinding -Name $ stagingSiteName -Port 80 -IPAddress * -HostHeader $liveWebsiteUrl -Protocol "http"   
And start it immediately
  1. Start-WebSite -Name $preliveSiteName  
I still think how to organize all the scripts, please let me know what do you think about Auto Deploy Framework based on PowerShell?



If you have any questions regarding the blog post - please let me know by leaving your comment, or via Twitter - @adoprog.

4 comments:

  1. We use MSBuild and Jenkins CI to configure automated build/deploy. I suppose PS would work as well but MSBuild is a language designed for build/deploy work.

    ReplyDelete
  2. The nice thing about PowerShell is that you can do everything with it. Update every single setting in IIS, work with databases just like you're using SQL Management Studio, etc.
    It becomes important when you need scalability across the servers, when the site is tightly integrated with lots of additional security / content providers and internal services.

    And at the same time, syntax is clear to C# developers. We used to have nAnt scripts before, and I see great difference.

    ReplyDelete
  3. Nice post. We currently use MSBuild for our deployment automation but increasingly I'm swayed to use either PowerShell in it's place or take the plunge with OctopusDeploy.

    Sitecore Rocks makes very effective use of PowerShell scripts to implement it's 'Create a new Sitecore web site' task. If you have the extension installed you can access them from:

    %localappdata%\Microsoft\VisualStudio\10.0\Extensions\Sitecore\Sitecore Rocks\0.7.12.1\Repositories\TaskLists\InstallSitecore

    Change the path to reflect the version of Visual Studio and the Rocks extension that you are working with.

    ReplyDelete
  4. We currently use MSBuild for project build and transformations, and have TeamCity run these along with Powershell scripts. It means we can create websites, perform db backups, allow IIS changes from settings file in project, so that developers need not access the staging environment at all.
    The more you can reduce the interaction for developers, the more you can reduce the risk of issues along the way.

    ReplyDelete