Click here to Skip to main content
15,879,535 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Created a build definition using TFS 2013 to run a scheduled build.
And using SetupSiteForPublish.ps1 script in post-build script process to deploy the site on iis on a remote server.

I am referring following 2 articles:

Article1 -http://chriskadel.com/2013/03/using-tfs-to-build-and-deploy-during-the-build-process-with-ms-deploy/

Article2 -http://www.iis.net/learn/publish/using-web-deploy/powershell-scripts-for-automating-web-deploy-setup

When i use /P:CreatePackageOnPublish=True /P:DeployOnBuild=True in MSBuild Arguments, getting following error message:

C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v12.0\Web\Microsoft.Web.Publishing.targets (3739): Package/Publish task Microsoft.Web.Publishing.Tasks.IsCleanMSDeployPackageNeeded failed to load Web Deploy assemblies. Microsoft Web Deploy is not correctly installed on this machine. Microsoft Web Deploy v3 or higher is recommended.


I have also tried reinstalling Web Deploy 3.5 on the server where i want to deploy the site, but it didn't fixed the issue.

---------Powershell script --------
JavaScript
<small></small>param(
	[parameter(Mandatory = $false)]
    $siteName,

	[parameter(Mandatory = $false)]
	$sitePhysicalPath,
	
	[parameter(Mandatory = $false)]
    $siteAppPoolName,
		
	[parameter(Mandatory = $false)]
	[int]$sitePort,
	
	[parameter(Mandatory = $false)]
	$deploymentUserName,
	
	[parameter(Mandatory = $false)]
	$deploymentUserPassword,
	
	[parameter(Mandatory = $false)]
	$managedRunTimeVersion,	
	
	[parameter(Mandatory = $false)]
	$publishSettingSavePath,
	
	[parameter(Mandatory = $false)]
	$publishSettingFileName
)

##Import-LocalizedData -BindingVariable Resources -FileName Resources.psd1

# ==================================


 #constants
 $SCRIPTERROR = 0
 $WARNING = 1
 $INFO = 2
 $logfile = ".\HostingLog-$(get-date -format MMddyyHHmmss).log"

$template = @"
<?xml version="1.0" encoding="utf-8"?>
<publishData>
  <publishProfile
    publishUrl=""
    msdeploySite=""
    destinationAppUrl=""
    mySQLDBConnectionString=""
    SQLServerDBConnectionString=""
    profileName="Default Settings"
    publishMethod="MSDeploy"
    userName=""
	userPWD=""
	savePWD="True"
	/>
</publishData>
"@

#the order is important. Check for apppool name first. Its possible that
#the user gave just a sitename to set permissions. In this case leave apppool emtpy.
#else give a default name to the apppool.
if(!$siteAppPoolName)

{
	if(!$siteName)
	{
		$siteAppPoolName = "AvailabilityServiceAgent"
	}
}
else	
{
	$siteAppPoolName = $siteAppPoolName.Trim()
}

#now the sitename check. If its empty give it a default name
if(!$siteName)
{
	$siteName = "AvailabilityServiceAgent"
}
else	
{
	$siteName = $siteName.Trim()
}

if(!$sitePhysicalPath)
{
    $sitePhysicalPath= $ENV:TF_BUILD_DROPLOCATION+"\_PublishedWebsites\AvailabilityServiceHost"
}
else
{
	$sitePhysicalPath = $sitePhysicalPath.Trim()
}

#global variable. Because we need to return two values from MWA from one function. [REF] has bugs. Hence global
$global:sitePath = $sitePhysicalPath
$global:publishURL = $null

# this function does logging
function write-log([int]$type, [string]$info){
        
    $message = $info -f $args
    $logMessage = get-date -format HH:mm:ss
    
	Switch($type){
		$SCRIPTERROR{
            $logMessage = $logMessage + "`t" + $Resources.Error + "`t" +  $message
			write-host -foregroundcolor white -backgroundcolor red $logMessage
		}
		$WARNING{
            $logMessage = $logMessage + "`t" + $Resources.Warning + "`t" +  $message
			write-host -foregroundcolor black -backgroundcolor yellow $logMessage
		}
		default{
            $logMessage = $logMessage + "`t" + $Resources.Info + "`t" +  $message
			write-host -foregroundcolor black -backgroundcolor green  $logMessage
		}
	}
        
	$logMessage >> $logfile
}


function GetPublishSettingSavePath()
{
	if(!$publishSettingFileName)
	{
		$publishSettingFileName = "WDeploy.PublishSettings"
	}
	
	if(!$publishSettingSavePath)
	{
		$publishSettingSavePath = [System.Environment]::GetFolderPath("Desktop")
	}

	if((test-path $publishSettingSavePath) -eq $false)
	{
		write-log $SCRIPTERROR $Resources.FailedToAccessScriptsFolder $publishSettingSavePath
		return $null
	}
	
	return Join-Path $publishSettingSavePath $publishSettingFileName
}

# returns false if OS is not server SKU
 function NotServerOS
 {
    $sku = $((gwmi win32_operatingsystem).OperatingSystemSKU)
    $server_skus = @(7,8,9,10,12,13,14,15,17,18,19,20,21,22,23,24,25)
    
    return ($server_skus -notcontains $sku)
 }

 # gives a user access to an IIS site's scope
 function GrantAccessToSiteScope($username, $websiteName)
{
    trap [Exception]
	{
        write-log $SCRIPTERROR $Resources.FailedToGrantUserAccessToSite $username $websiteName
		return $false
    }
	
	foreach($mInfo in [Microsoft.Web.Management.Server.ManagementAuthorization]::GetAuthorizedUsers($websiteName, $false, 0,[int]::MaxValue))
	{
		if($mInfo.Name -eq $username)
		{
			write-log $INFO $Resources.UserHasAccessToSite $username $websiteName
			return $true
		}
	}
	
	[Microsoft.Web.Management.Server.ManagementAuthorization]::Grant($username, $websiteName, $FALSE) | out-null
	write-log $INFO $Resources.GrantedUserAccessToSite $username $websiteName
	return $true
 }
 
 # gives a user permissions to a file on disk 
 function GrantPermissionsOnDisk($username, $type, $options)
 {
	trap [Exception]
	{
        write-log $SCRIPTERROR $Resources.NotGrantedPermissions $type $username $global:sitePath
    }

	$acl = (Get-Item $global:sitePath).GetAccessControl("Access")
	$accessrule = New-Object system.security.AccessControl.FileSystemAccessRule($username, $type, $options, "None", "Allow")
	$acl.AddAccessRule($accessrule)
	set-acl -aclobject $acl $global:sitePath
	write-log $INFO $Resources.GrantedPermissions $type $username $global:sitePath
}
 
 function AddUser($username, $password)
 {
    if(-not (CheckLocalUserExists($username) -eq $true))
    {
        $comp = [adsi] "WinNT://$env:computername,computer" 
		$user = $comp.Create("User", $username)   
		$user.SetPassword($password)
		$user.SetInfo()
        write-log $INFO $Resources.CreatedUser $username
    }
 }
  
 function CheckLocalUserExists($username)
 {
	$objComputer = [ADSI]("WinNT://$env:computername")
	$colUsers = ($objComputer.psbase.children | Where-Object {$_.psBase.schemaClassName -eq "User"} | Select-Object -expand Name)

	$blnFound = $colUsers -contains $username

	if ($blnFound){
		return $true
	}
	else{
		return $false
	}
 }
 
 function CheckIfUserIsAdmin($username)
 {
    $computer = [ADSI]("WinNT://$env:computername,computer")  
    $group = $computer.psbase.children.find("Administrators") 

    $colMembers = $group.psbase.invoke("Members") | %{$_.GetType().InvokeMember("Name",'GetProperty',$null,$_,$null)} 
    
    $bIsMember = $colMembers -contains $username
    if($bIsMember)
    {
        return $true
    }
    else
    {
        return $false
    }
 }
 
 function CreateLocalUser($username, $password, $isAdmin)
 {
	AddUser $username $password
	
	if($isAdmin)
    {
        if(-not(CheckIfUserIsAdmin($username) -eq $true))
        {
    		$group = [ADSI]"WinNT://$env:computername/Administrators,group"
    		$group.add("WinNT://$env:computername/$username")
    		write-log $INFO $Resources.AddedUserAsAdmin $username
    	}
        else
        {
            write-log $INFO $Resources.IsAdmin $username
        }
    }

    return $true
 }
 
 function Initialize
 {
    trap [Exception]
    {
        write-log $SCRIPTERROR $Resources.CheckIIS7Installed
        break
    }
    
    $inetsrvPath = ${env:windir} + "\system32\inetsrv\"
    
    [System.Reflection.Assembly]::LoadFrom( $inetsrvPath + "Microsoft.Web.Administration.dll" ) > $null
    [System.Reflection.Assembly]::LoadFrom( $inetsrvPath + "Microsoft.Web.Management.dll" )   > $null 
 }  
 
 function GetPublicHostname()
{
	$ipProperties = [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties()
	if($ipProperties.DomainName -eq "")
	{
		return $ipProperties.HostName
	}
	else
	{
		return "{0}.{1}" -f $ipProperties.HostName, $ipProperties.DomainName
	}
}

function GenerateStrongPassword()
{
   [System.Reflection.Assembly]::LoadWithPartialName("System.Web") > $null
   return [System.Web.Security.Membership]::GeneratePassword(12,4)
}

function GetPublishURLFromBindingInfo($bindingInfo, $protocol, $hostname)
{
	$port = 80
	trap [Exception]
    {
		#return defaults		
		return "http://$hostname"
    }
	
	if(($bindingInfo -match "(.*):(\d*):([^:]*)$") -and
		($Matches.Count -eq 4 ))
	{
		$port = $Matches[2]
		$header = $Matches[3]
		$ipaddress = $Matches[1]
		if($header)
		{
			$hostname = $header
		}
		elseif(($ipaddress) -AND (-not($ipaddress -eq "*")))
		{
			$bracketsArray = @('[',']')
			$hostname  = $ipaddress.Trim($bracketsArray)
		}
		
		if(-not($port -eq 80))
		{
			$hostname = $hostname + ":" + $port
		}
	}
	
	return $protocol + "://" + $hostname
}


function GetUnusedPortForSiteBinding()
{
	[int[]] $portArray = $null
	$serverManager = (New-Object Microsoft.Web.Administration.ServerManager) 
	foreach($site in $serverManager.Sites)
	{
		foreach($binding in $site.Bindings)
		{
			if($binding.IsIPPortHostBinding)
			{
				if($binding.Protocol -match "https?")
				{
					if(($binding.BindingInformation -match "(.*):(\d*):([^:]*)$") -and
					($Matches.Count -eq 4 ))
					{
						$portArray = $portArray + $Matches[2]
					}
				}
			}
		}
	}
	
	if(-not($portArray -eq $null))
	{
		$testPortArray = 8081..8200
		foreach($port in $testPortArray)
		{
			if($portArray -notcontains $port)
			{
				return $port
			}
		}
	}
	
	return 8081 #default
}

function CreateSite($name, $appPoolName, $port, $dotnetVersion)
{
	trap [Exception]
    {
		write-log $SCRIPTERROR $Resources.SiteCreationFailed
        return $false
    }
	
	$hostname = GetPublicHostName
	$global:publishURL = "http://$hostname"
	if(-not($port -eq 80))
	{
		$global:publishURL = $global:publishURL + ":" + $port
	}
	
	$configHasChanges = $false
	$serverManager = (New-Object Microsoft.Web.Administration.ServerManager) 
	
	#appPool might be empty. WHen the user gave just a site name to 
	#set the permissions on. As long as the sitename is not empty
	if($appPoolName)
	{
		$appPool = $serverManager.ApplicationPools[$appPoolName]
		if ($appPool -eq $null)
		{
			$appPool = $serverManager.ApplicationPools.Add($appPoolName)
			$appPool.Enable32BitAppOnWin64 = $true
				
			if( ($dotnetVersion) -and 
			(CheckVersionWithinAllowedRange $dotnetVersion) )
			{
				$appPool.ManagedRuntimeVersion = $dotnetVersion
			}
			$configHasChanges = $true
			write-log $INFO $Resources.AppPoolCreated $appPoolName
		}
		else
		{
			write-log $WARNING $Resources.AppPoolExists $appPoolName
		}
	}

	$newSite = $serverManager.Sites[$name]
	if ($newSite -eq $null)
	{
		$newSite = $serverManager.Sites.Add($name,$global:sitePath, $port)
		$newSite.Applications[0].ApplicationPoolName = $appPool.Name
		if((test-path $global:sitePath) -eq $false)
		{
			[System.IO.Directory]::CreateDirectory($global:sitePath)
		}
		else
		{
			write-log $WARNING $Resources.SiteVirtualDirectoryExists $global:sitePath
		}
		
		$newSite.ServerAutoStart = $true
		$configHasChanges = $true
		write-log $INFO $Resources.SiteCreated $name
	}
	else
	{
		#get virtual directory and siteport
		$global:sitePath = [System.Environment]::ExpandEnvironmentVariables($newSite.Applications["/"].VirtualDirectories["/"].PhysicalPath)
		
		foreach($binding in $newSite.Bindings)
		{
			if($binding.IsIPPortHostBinding)
			{
				if($binding.Protocol -match "https?")
				{
					$global:publishURL = GetPublishURLFromBindingInfo $binding.BindingInformation $binding.Protocol $hostname
				}
			}
		}
		
		if($appPoolName)
		{
			if (-not($newSite.Applications[0].ApplicationPoolName -eq $appPool.Name ))
			{
				$newSite.Applications[0].ApplicationPoolName = $appPool.Name
				write-log $INFO $Resources.SiteAppPoolUpdated $name $appPoolName
			}
			else
			{			
				write-log $INFO $Resources.SiteExists $name $appPoolName
			}
		}
		else
		{
			write-log $INFO $Resources.SiteExists $name $newSite.Applications[0].ApplicationPoolName
		}
	}

	if ($configHasChanges)
	{
		$serverManager.CommitChanges()
	}	
	
	return $true
}

function CheckUserViaLogon($username, $password)
 {
 
 $signature = @'
	[DllImport("advapi32.dll")]
	public static extern int LogonUser(
		string lpszUserName,
		string lpszDomain,
		string lpszPassword,
		int dwLogonType,
		int dwLogonProvider,
		ref IntPtr phToken);
'@ 
 	
 	$type = Add-Type -MemberDefinition $signature  -Name Win32Utils -Namespace LogOnUser  -PassThru
	
	[IntPtr]$token = [IntPtr]::Zero

	$value = $type::LogOnUser($username, $env:computername, $password, 2, 0, [ref] $token)

	if($value -eq 0)
	{
		return $false
	}
	
	return $true
 }

function CheckUsernamePasswordCombination($user, $password)
 {
	if(($user) -AND ($password))
	{
		if(CheckLocalUserExists($user) -eq $true)
		{
			if(CheckUserViaLogon $user $password)
			{
				return $true
			}
			else
			{
				write-Log $SCRIPTERROR $Resources.FailedToValidateUserWithSpecifiedPassword $user
				return $false
			}
		}		
	}
	
	return $true
 }
 
 function CreateProfileXml([string]$nameofSite, [string]$username, $password, [string]$hostname, $pathToSaveFile)
{
	trap [Exception]
    {
		write-log $SCRIPTERROR $Resources.FailedToWritePublishSettingsFile $pathToSaveFile
		return
	}
	
	$xml = New-Object xml
	
	if(Test-Path $pathToSaveFile)
	{
		$xml.Load($pathToSaveFile)
	}
	else
	{
		$xml.LoadXml($template)
	}

	$newProfile = (@($xml.publishData.publishProfile)[0])
	$newProfile.publishUrl = $hostname
	$newProfile.msdeploySite = $nameofSite

	$newProfile.destinationAppUrl = $global:publishURL.ToString()
	$newProfile.userName = $username

	if(-not ($password -eq $null))
	{
		$newProfile.userPWD = $password.ToString()
	}
	else
	{
		write-log $WARNING $Resources.NoPasswordForExistingUserForPublish
	}
	
	$xml.Save($pathToSaveFile)
	
	write-log $INFO $Resources.SavingPublishXmlToPath $pathToSaveFile
}

function CheckVersionWithinAllowedRange($managedVersion)
{
	trap [Exception]
    {
		return $false
	}
	
	$KeyPath = "HKLM:\Software\Microsoft\.NETFramework"
	$key = Get-ItemProperty -path $KeyPath
	$path = $key.InstallRoot
	$files = [System.IO.Directory]::GetFiles($path, "mscorlib.dll", [System.IO.SearchOption]::AllDirectories)
	foreach($file in $files)
	{
		if($file -match "\\(v\d\.\d).\d*\\")
		{
			if($Matches[1] -eq $managedVersion)
			{
				return $true
			}
		}
	}
	return $false
}
 

#================= Main Script =================

 if(NotServerOS)
 {
    write-log $SCRIPTERROR $Resources.NotServerOS
    break
 }

Initialize
if(CheckUsernamePasswordCombination $deploymentUserName $deploymentUserPassword)
{
	if(!$sitePort)
	{
		$sitePort = GetUnusedPortForSiteBinding 
	}
	if(CreateSite $siteName $siteAppPoolName $sitePort $managedRunTimeVersion)
	{		
		if(!$deploymentUserName)
		{
			$idx = $siteName.IndexOf(' ')
			if( ($idx -gt 0) -or ($siteName.Length -gt 16))
			{
				$deploymentUserName = "WDeployuser"
			}
			else
			{
				$deploymentUserName = $siteName + "user"
			}
		}
		
		if( (CheckLocalUserExists($deploymentUserName) -eq $true))
		{
			$deploymentUserPassword = $null
		}
		else
		{
			if(!$deploymentUserPassword)
			{
				$deploymentUserPassword = GenerateStrongPassword
			}
		}
		
		
		if(CreateLocalUser $deploymentUserName $deploymentUserPassword $false)
		{
			GrantPermissionsOnDisk $deploymentUserName "FullControl" "ContainerInherit,ObjectInherit"
			
			if(GrantAccessToSiteScope ($env:computername + "\" + $deploymentUserName) $siteName)
			{
				$hostname = GetPublicHostName
				$savePath = GetPublishSettingSavePath
				if($savePath)
				{
					CreateProfileXml $siteName $deploymentUserName $deploymentUserPassword $hostname $savePath
				}
			}
		}
	}
}
Write-Host "Post-build script succeeded!"
Posted
Comments
Kornfeld Eliyahu Peter 9-Jun-15 7:11am    
And what prevent from you to upgrade Web Deploy?
http://www.iis.net/downloads/microsoft/web-deploy
JainPrince 9-Jun-15 7:42am    
Peter thank you for response.
The error says V3 or higher...but already Web Deploy 3.5 is installed.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900