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.