Deploying a .Net Web Application to IIS with Powershell

Overview:

Below is a script which compiles a .Net C# web application, connects to a remote Windows Server, installs IIS and then installs the web application! This script is meant to maintain multiple test environments spread across different machines. MSDeploy performs similar functions, but I haven’t been impressed with the error messages and documentation of MSDeploy. My hope is that PowerShell can be a long-term deploy tool.

Challenges:

For this I am running the powershell script on Windows 7 Professional with administrator priviliges and I am deploying the application to IIS 7.5 on a Windows Server 2008 R2 virtual box VM. With Virtual Box you need to:

  1. Configure the virtual box network adapter to be a “Bridged Adapter”.
  2. Run this powershell script: http://sharepoint.smayes.com/2012/02/virtualbox-unidentified-network/
  3. I had to add a password to my Windows 7 administrator account. This is likely best practices anyway, but it’s not something I typically do on my home machine.

Rant:

This post was almost named “I Hate Powershell!”. It took me hours to figure out the simplest things. Powershell is certainly not as simple as I would have thought. Simple tasks are very confusing – such as creating a zip file. With Powershell I didn’t seem to be asking the right questions to solve my problems -I usually had to use solutions that didn’t seem natural. It gives me the feeling that Powershell is not well suited for what I’m trying to do, which is unfortunate because automating deployments seems like a standard scripting task.  I did figure it all out, but as I said earlier, this simple script took many, many more hours of experimenting than I would have expected. In the end I did learn a great deal about powershell so I think it was worth the effort.

Configuration required:

On both the destination machine and the source machine:

1. Type the following in powershell:

This will configure Powershell Remoting and make sure appropriate firewall ports are open. Powershell remoting is needed for installing IIS and unzipping the application.


winrm quickconfig
winrm s winrm/config/client '@{TrustedHosts="*"}'

2. Verify BITS (Background Intelligent Tranfer Service) and winrm (Windows Remote Management) are running

BITS is used to transfer a zipped copy of the web site to the remote machine.


gsv bits
gsv winrm

The Script!

So I must admit… I did get a little lazy towards the end. It’s far from perfect, but it’s easy to understand, it is a complete solution and it is easy enough to work with.

</pre>
function ZipMe {
 Param([string]$path)

if (-not $path.EndsWith('.zip')) {$path += '.zip'}

if (-not (test-path $path)) {
 set-content $path ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18))
 }
 $ZipFile = (new-object -com shell.application).NameSpace($path)
 #$input | foreach {$zipfile.CopyHere($_.fullname, 20)}

foreach($file in $input)
 {
 $zipfile.CopyHere($file.FullName, 20)
 # Without this it throws errors. I'm not sure why.
 Start-sleep -milliseconds 500
 }
}

Function Get-PSCredential($User,$Password)
{
 $SecPass = convertto-securestring -asplaintext -string $Password -force
 $Creds = new-object System.Management.Automation.PSCredential -argumentlist $User,$SecPass
 Return $Creds
}
# Build Website
& "C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe" "C:\git\simple-website\simple-website.sln"

if(Test-Path "c:\Admin.zip") {
 # Delete Existing zip file
 Remove-Item "c:\Admin.zip" -Force
}

# Zip Website
dir "C:\git\simple-website\AdminSite\" | ZipMe c:\Admin.zip

$credential = Get-PSCredential -User "someuser" -Password "secretpassword"

$session = New-PSSession 192.168.1.68 -Credential $credential

#Remove file if it exists
invoke-command -session $session -scriptblock {

if(Test-Path "c:\Admin.zip") {
 # Delete Existing zip file
 Remove-Item "c:\Admin.zip" -Force
}
}

Import-Module BitsTransfer
Start-BitsTransfer -source C:\Admin.zip -destination \\192.168.1.68\c$\ -credential $credential

invoke-command -session $session -scriptblock {

Set-ExecutionPolicy RemoteSigned

function UnZipMe($zipfilename, $destination)
{
 $shellApplication = new-object -com shell.application
 $zipPackage = $shellApplication.NameSpace($zipfilename)
 $destinationFolder = $shellApplication.NameSpace($destination)

# CopyHere vOptions Flag # 4 - Do not display a progress dialog box.
# 16 - Respond with "Yes to All" for any dialog box that is displayed.

$destinationFolder.CopyHere($zipPackage.Items(),20)
}
# Install IIS if required
Import-Module Servermanager

$check = Get-WindowsFeature | Where-Object {$_.Name -eq "web-server"}

If (!($check.Installed)) {
 Write-Host "Adding web-server"
 Add-WindowsFeature web-server
}

$name = "Admin"
$physicalPath = "C:\inetpub\wwwroot\" + $name

# Create Application Pool
try
{
 $poolCreated = Get-WebAppPoolState $name –errorvariable myerrorvariable
 Write-Host $name "Already Exists"
}
catch
{
 # Assume it doesn't exist. Create it.
 New-WebAppPool -Name $name
 Set-ItemProperty IIS:\AppPools\$name managedRuntimeVersion v4.0
}

# Create Folder for the website
if(!(Test-Path $physicalPath)) {
 md $physicalPath
}
else {
 Remove-Item "$physicalPath\*" -recurse -Force
}

$site = Get-WebSite | where { $_.Name -eq $name }
if($site -eq $null)
{
 Write-Host "Creating site: $name $physicalPath"

 # TODO:
 New-WebSite $name | Out-Null
 New-WebApplication -Site $name -Name $name -PhysicalPath "C:\inetpub\wwwroot\Admin" -ApplicationPool $name
}

UnZipMe -zipfilename "c:\Admin.zip" -destination "C:\inetpub\wwwroot\Admin"

}

Remove-PSSession $session

The Goods

Advertisements
This entry was posted in .Net, IIS and tagged , , , , , . Bookmark the permalink.

3 Responses to Deploying a .Net Web Application to IIS with Powershell

  1. Sebastian Cole says:

    Did you have any errors using BITS? When I try to use it in our build process I ran into problems where BITS required the running security context to be in an interactive session. This made it pretty useless as an automated tool for me :\

    Start-BitsTransfer : The operation being requested was not performed because th
    e user has not logged on to the network. The specified service does not exist.
    (Exception from HRESULT: 0x800704DD)

    • codealoc says:

      I didn’t have problems with it only running in an interactive mode. By default powershell only allows running scripts in interactive mode. You have probably already changed the Set-ExecutionPolicy to Unrestricted – if not definitely do that. Also try running the script as an Administrator or with different credentials in Get-PSCredential. What credentials is the build process using? The user the build service is running ~ CruiseControl, Jenkins, etc will need access to the machine you are copying files too.

  2. gsdn says:

    I’m giving occasional help to a guy writing an (overly complex, IMHO) deployment system in powershell, hooked on Jenkins and SVN, and he just hit this yesterday: BITS really requires the job owner to be logged on… it IS that frustrating (http://msdn.microsoft.com/en-us/library/windows/desktop/aa363159(v=vs.85).aspx)

    BTW, Powershell is really amazing and a long needed tool for windows administration. But it is frustrating that MS took so long to come up with such a tool, and it still has some protuberant rough edges. Another frustration is remote sessions, which while having impressive features, lacks such basic things as file transfer, and then BITS is also frustrating by itself…

    Good luck!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s