Unpacking Software Livestream

Join our monthly Unpacking Software livestream to hear about the latest news, chat and opinion on packaging, software deployment and lifecycle management!

Learn More

Chocolatey Product Spotlight

Join the Chocolatey Team on our regular monthly stream where we put a spotlight on the most recent Chocolatey product releases. You'll have a chance to have your questions answered in a live Ask Me Anything format.

Learn More

Chocolatey Coding Livestream

Join us for the Chocolatey Coding Livestream, where members of our team dive into the heart of open source development by coding live on various Chocolatey projects. Tune in to witness real-time coding, ask questions, and gain insights into the world of package management. Don't miss this opportunity to engage with our team and contribute to the future of Chocolatey!

Learn More

Calling All Chocolatiers! Whipping Up Windows Automation with Chocolatey Central Management

Webinar from
Wednesday, 17 January 2024

We are delighted to announce the release of Chocolatey Central Management v0.12.0, featuring seamless Deployment Plan creation, time-saving duplications, insightful Group Details, an upgraded Dashboard, bug fixes, user interface polishing, and refined documentation. As an added bonus we'll have members of our Solutions Engineering team on-hand to dive into some interesting ways you can leverage the new features available!

Watch On-Demand
Chocolatey Community Coffee Break

Join the Chocolatey Team as we discuss all things Community, what we do, how you can get involved and answer your Chocolatey questions.

Watch The Replays
Chocolatey and Intune Overview

Webinar Replay from
Wednesday, 30 March 2022

At Chocolatey Software we strive for simple, and teaching others. Let us teach you just how simple it could be to keep your 3rd party applications updated across your devices, all with Intune!

Watch On-Demand
Chocolatey For Business. In Azure. In One Click.

Livestream from
Thursday, 9 June 2022

Join James and Josh to show you how you can get the Chocolatey For Business recommended infrastructure and workflow, created, in Azure, in around 20 minutes.

Watch On-Demand
The Future of Chocolatey CLI

Livestream from
Thursday, 04 August 2022

Join Paul and Gary to hear more about the plans for the Chocolatey CLI in the not so distant future. We'll talk about some cool new features, long term asks from Customers and Community and how you can get involved!

Watch On-Demand
Hacktoberfest Tuesdays 2022

Livestreams from
October 2022

For Hacktoberfest, Chocolatey ran a livestream every Tuesday! Re-watch Cory, James, Gary, and Rain as they share knowledge on how to contribute to open-source projects such as Chocolatey CLI.

Watch On-Demand

Downloads:

39,423

Downloads of v 2.2.75:

346

Last Update:

23 Sep 2014

Package Maintainer(s):

Software Author(s):

  • Jayme Edwards

Tags:

admin

PowerDelivery

This is not the latest version of PowerDelivery available.

  • 1
  • 2
  • 3

2.2.75 | Updated: 23 Sep 2014

Downloads:

39,423

Downloads of v 2.2.75:

346

Maintainer(s):

Software Author(s):

  • Jayme Edwards

Tags:

admin

PowerDelivery 2.2.75

This is not the latest version of PowerDelivery available.

  • 1
  • 2
  • 3

Some Checks Have Failed or Are Not Yet Complete

Not All Tests Have Passed


Validation Testing Unknown


Verification Testing Unknown


Scan Testing Successful:

No detections found in any package files

Details
Learn More

Deployment Method: Individual Install, Upgrade, & Uninstall

To install PowerDelivery, run the following command from the command line or from PowerShell:

>

To upgrade PowerDelivery, run the following command from the command line or from PowerShell:

>

To uninstall PowerDelivery, run the following command from the command line or from PowerShell:

>

Deployment Method:

NOTE

This applies to both open source and commercial editions of Chocolatey.

1. Enter Your Internal Repository Url

(this should look similar to https://community.chocolatey.org/api/v2/)


2. Setup Your Environment

1. Ensure you are set for organizational deployment

Please see the organizational deployment guide

2. Get the package into your environment

  • Open Source or Commercial:
    • Proxy Repository - Create a proxy nuget repository on Nexus, Artifactory Pro, or a proxy Chocolatey repository on ProGet. Point your upstream to https://community.chocolatey.org/api/v2/. Packages cache on first access automatically. Make sure your choco clients are using your proxy repository as a source and NOT the default community repository. See source command for more information.
    • You can also just download the package and push it to a repository Download

3. Copy Your Script

choco upgrade powerdelivery -y --source="'INTERNAL REPO URL'" --version="'2.2.75'" [other options]

See options you can pass to upgrade.

See best practices for scripting.

Add this to a PowerShell script or use a Batch script with tools and in places where you are calling directly to Chocolatey. If you are integrating, keep in mind enhanced exit codes.

If you do use a PowerShell script, use the following to ensure bad exit codes are shown as failures:


choco upgrade powerdelivery -y --source="'INTERNAL REPO URL'" --version="'2.2.75'" 
$exitCode = $LASTEXITCODE

Write-Verbose "Exit code was $exitCode"
$validExitCodes = @(0, 1605, 1614, 1641, 3010)
if ($validExitCodes -contains $exitCode) {
  Exit 0
}

Exit $exitCode

- name: Install powerdelivery
  win_chocolatey:
    name: powerdelivery
    version: '2.2.75'
    source: INTERNAL REPO URL
    state: present

See docs at https://docs.ansible.com/ansible/latest/modules/win_chocolatey_module.html.


chocolatey_package 'powerdelivery' do
  action    :install
  source   'INTERNAL REPO URL'
  version  '2.2.75'
end

See docs at https://docs.chef.io/resource_chocolatey_package.html.


cChocoPackageInstaller powerdelivery
{
    Name     = "powerdelivery"
    Version  = "2.2.75"
    Source   = "INTERNAL REPO URL"
}

Requires cChoco DSC Resource. See docs at https://github.com/chocolatey/cChoco.


package { 'powerdelivery':
  ensure   => '2.2.75',
  provider => 'chocolatey',
  source   => 'INTERNAL REPO URL',
}

Requires Puppet Chocolatey Provider module. See docs at https://forge.puppet.com/puppetlabs/chocolatey.


4. If applicable - Chocolatey configuration/installation

See infrastructure management matrix for Chocolatey configuration elements and examples.

WARNING

This package was submitted prior to moderation and has not been approved. While it is likely safe for you, there is more risk involved.

Description

A deployment framework helping you to continuously deliver software to your customers using Microsoft Team Foundation Server and Windows PowerShell.


tools\chocolateyInstall.ps1
try { 
    $powerdeliveryDir = Split-Path -parent $MyInvocation.MyCommand.Definition
  
    Write-Host "Updating PSModulePath to include $powerdeliveryDir..."

    $psModulePath = (Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PSModulePath).PSModulePath
  
    $newEnvVar = $powerdeliveryDir

    $caseInsensitive = [StringComparison]::InvariantCultureIgnoreCase

    $pathSegment = "chocolatey\lib\powerdelivery"

    if (![String]::IsNullOrWhiteSpace($psModulePath)) {
        if ($psModulePath.IndexOf($pathSegment, $caseInsensitive) -lt 0) { # First time installing
			if ($psModulePath.EndsWith(";")) {
				$psModulePath = $psModulePath.TrimEnd(";")
			}
            $newEnvVar = "$($psModulePath);$($powerdeliveryDir)"
        }
        else { # Replacing an existing install
            $indexOfSegment = $psModulePath.IndexOf($pathSegment, $caseInsensitive)
            $startingSemicolon = $psModulePath.LastIndexOf(";", $indexOfSegment, $caseInsensitive)
            $trailingSemicolon = $psModulePath.IndexOf(";", $indexOfSegment + $pathSegment.Length, $caseInsensitive)

            if ($startingSemicolon -ne -1) {
                $psModulePrefix = $psModulePath.Substring(0, $startingSemicolon)
				$newEnvVar = "$($psModulePrefix);$($powerdeliveryDir)"
			}			
			if ($trailingSemicolon -ne -1) {
                $newEnvVar += $psModulePath.Substring($trailingSemicolon)
            }
        }
    }
	
	Start-ChocolateyProcessAsAdmin @"
		Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name 'PSModulePath' -Value '$newEnvVar'
"@

	Update-SessionEnvironment

	$env:PSModulePath = $newEnvVar

    Write-ChocolateySuccess 'powerdelivery'
} 
catch {
    Write-ChocolateyFailure 'powerdelivery' "$($_.Exception.Message)"
    throw 
}
tools\enable-buildDeployment.ps1
<#
Enable-BuildDeployment.ps1

Sets up a computer to be remotely deployed to and managed 
by a TFS build agent for continuous delivery.
#>

param(
  [Parameter(Mandatory=1)][string] $buildAgentComputer,
  [Parameter(Mandatory=1)][string] $buildUserName,
  [Parameter(Mandatory=1)][string] $buildUserDomain
)

if ($(get-host).version.major -lt 3) {
  "Powershell 3.0 or greater is required."
  exit
}

$localComputer = gc env:computername

$currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
if (!(New-Object Security.Principal.WindowsPrincipal $currentUser).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)) {
  "Please re-run the PowerShell console as Administrator before running this script."
  exit
}

$localAdminGroup = [ADSI]"WinNT://$localComputer/Administrators,group" 
$localAdminSet = [ADSI]"WinNT://$localComputer/Administrators"
$localAdminGroupMembers = @($localAdminSet.psbase.Invoke("Members"))

$isMemberOfAdminGroup = $false

"Checking if $buildUserDomain\$buildUserName is a member of the local Administrators group..."

$localAdminGroupMembers | foreach {
    if ($_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null) -eq "$buildUserDomain\$buildUserName") {
        $isMemberOfAdminGroup = $true
    }
}

if ($isMemberOfAdminGroup -eq $false) {
    "Adding $buildUserDomain\$buildUserName to the local Administrators group..."
    $localAdminGroup.psbase.Invoke("Add",([ADSI]"WinNT://$buildUserDomain/$buildUserName").path)
}

"Performing WinRM quick configuration..."

winrm quickconfig -Force

"Enabling PowerShell Remoting..."

Enable-PSRemoting -force

"Permitting $buildAgentComputer to issue commands to $localComputer..."

$winRmConfigParams = '@{TrustedHosts="buildAgentComputer"}' -Replace "buildAgentComputer", "$buildAgentComputer"

winrm set winrm/config/client $winRmConfigParams
tools\HyperVDeliveryModule\HyperVDeliveryModule.psd1
 
tools\HyperVDeliveryModule\HyperVDeliveryModule.psm1
function Initialize-HyperVDeliveryModule {
	<#
	Register-DeliveryModuleHook 'PreSetupEnvironment' {

		$modulesFolder = Get-BuildDeliveryModulesFolder
		$projectsFile = Join-Path $modulesFolder "HyperV.csv"
		
		if (Test-Path $hyperVFile) {
			Import-Csv $hyperVFile | ForEach-Object {
				$invokeArgs = @{}
				
				$vmComputer = "localhost"
				$vmName = "PowerDeliveryASPNETMVC4_Dev"
				$vmMemory = "1073741824" # 1gb
				$vmSize = "21474836480" #20gb
				$vmNewVHDPath = "D:\VMs\Hyper-V\PowerDeliveryASPNETMVC4_Dev\Virtual Hard Disks\PowerDeliveryASPNETMVC4_Dev.vhdx"
				$vmRootPath = "D:\VMs\Hyper-V"
				
				if ($_.ProjectFile) {
					$invokeArgs.Add('projectFile', $_.ProjectFile)
				}
				
				if ($_.Target) {
					$invokeArgs.Add('target', $_.Target)
				}
				
				if ($_.Target) {
					$invokeArgs.Add('target', $_.Target)
				}
				
				if ($_.ToolsVersion) {
					$invokeArgs.Add('verbosity', $_.Verbosity)
				}
				
				if ($_.BuildConfig) {
					$invokeArgs.Add('target', $_.Target)
				}
				
				if ($_.Properties) {
					$properties = ConvertFrom-StringData $_.Properties
					$invokeArgs.Add('properties', $properties)
				}
				
				& Invoke-MSBuild @invokeArgs
				
				
				$vm = Get-VM -Name $vmName -ErrorAction Ignore
				if ($vm) {
					"Found existing virtual Machine $vmName."
				}
				else {
					"Creating virtual machine $vmName..."
					$vm = New-VM -ComputerName $vmComputer -Name $vmName `
						   -MemoryStartupBytes $vmMemory -NewVHDSizeBytes $vmSize `
						   -NewVHDPath $vmVHDPath -Path $vmPath
				}
				
			}
		}
	}#>
}
tools\init.ps1
param($installPath, $toolsPath, $package)

$powerdeliveryModule = Join-Path $toolsPath PowerDelivery.psm1
import-module $powerdeliveryModule
tools\MSBuildDeliveryModule\MSBuildDeliveryModule.psd1
 
tools\MSBuildDeliveryModule\MSBuildDeliveryModule.psm1
function Initialize-MSBuildDeliveryModule {

	Register-DeliveryModuleHook 'PreCompile' {
	
		$moduleConfig = Get-BuildConfig
		$msBuildProjects = $moduleConfig.MSBuild
		
		$currentDirectory = Get-Location

		if ($msBuildProjects) {
			
			$msBuildProjects.Keys | % {
				$invokeArgs = @{}
				
				$project = $msBuildProjects[$_]
				
				Invoke-BuildConfigSection $project "Invoke-MSBuild"	
				
				Set-Location $currentDirectory
			}
		}
	}
}
tools\MSTestDeliveryModule\MSTestDeliveryModule.psd1
 
tools\MSTestDeliveryModule\MSTestDeliveryModule.psm1
function RegisterMSTestHook {
	[CmdletBinding()]
	param(
		[Parameter(Position=0,Mandatory=1)] $testSectionName
	)

	$moduleConfig = Get-BuildConfig
	$msTestSection = $moduleConfig.MSTest
	
	if ($msTestSection) {
		$testSection = $msTestSection[$testSectionName]
		if ($testSection) {
		
			Invoke-BuildConfigSections $testSection "Invoke-MSTest"
		}
	}
}

function Initialize-MSTestDeliveryModule {

	Register-DeliveryModuleHook 'PreTestUnits' {
		RegisterMSTestHook 'UnitTests'
	}
	
	Register-DeliveryModuleHook 'PreTestAcceptance' {
		RegisterMSTestHook 'AcceptanceTests'
	}
}
tools\PowerDelivery\Add-CommandCredSSP.ps1
function Add-CommandCredSSP {
    param(
        [Parameter(Position=0,Mandatory=1)][string] $computerName, 
        [Parameter(Position=1,Mandatory=1)] $invokeArgs,
        [Parameter(Position=2,Mandatory=0)][string] $credentialUserName
    )

    if (!$computerName.StartsWith("localhost")) {

        $invokeArgs.Add("ComputerName", $computerName)
        $invokeArgs.Add("Authentication", "Credssp")

        Add-CredSSPTrustedHost $computerName

        $credentials = Get-BuildCredentials $credentialUserName
        $invokeArgs.Add("Credential", $credentials)
    }
}
tools\PowerDelivery\Add-CredSSPTrustedHost.ps1
function Add-CredSSPTrustedHost {
    param(
        [Parameter(Position=0,Mandatory=1)] [string] $computerName
    )

    $logPrefix = "Add-CredSSPTrustedHost:"

    Invoke-Command -ComputerName $computerName -ArgumentList @($logPrefix) -ScriptBlock {
        param($varLogPrefix)
        Write-Host "$varLogPrefix Enabling $($env:COMPUTERNAME) to receive remote CredSSP credentials"
        Enable-WSManCredSSP -Role Server -Force | Out-Null
    }

    $credSSP = Get-WSManCredSSP

    $computerExists = $false

    if ($credSSP -ne $null) {
        if ($credSSP.length -gt 0) {
            $trustedClients = $credSSP[0].Substring($credSSP[0].IndexOf(":") + 2)
            $trustedClientsList = $trustedClients -split "," | % { $_.Trim() }
            
            if ($trustedClientsList.Contains("wsman/$computerName")) {
                $computerExists = $true
            }
        }
    }

    if (!$computerExists) {
        "$logPrefix Enabling CredSSP credentials to travel from $($env:COMPUTERNAME) to $computerName"
        Enable-WSManCredSSP -Role Client -DelegateComputer $computerName -Force | Out-Null
    }
}
tools\PowerDelivery\Add-ExamplePipeline.ps1
<#
.Synopsis
Adds a powerdelivery build pipeline to a TFS project from an example.

.Description
You need an existing TFS project that is empty already created and to have 
rights as a Project Administrator (to create builds, security groups, 
and add files to source control) to run this cmdlet.

.Example
Add-ExamplePipeline -example "ProductStoreTabular" -collection "http://your-tfsserver/tfs" -project "My Project" -controller "MyController" -dropFolder "\\SERVER\share"

.Parameter example
Optional. The name of an example folder in the Examples subdirectory of a powerdelivery Chocolatey installation. Either this or the examplePath parameter must be supplied.

.Parameter examplePath
Optional. The path to a custom example. Can be used to create new deployment pipelines from add your own starter projects. Either this or the example parameter must be supplied.

.Parameter outputPath
The path to map your working folder to work with the example.

.Parameter collection
The URI of the TFS collection to add the example to.

.Parameter project
The TFS project to add the example to.

.Parameter dropFolder
The folder compiled assets should go into from the pipeline.

.Parameter controller
The name of the TFS build controller the pipeline should use.

.Parameter vsVersion
Which version of the Visual Studio command-line tools to load for calling the TFS API. Set to "10.0" by default.
#>
function Add-ExamplePipeline {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=0)][string] $example,
        [Parameter(Mandatory=0)][string] $examplePath,
        [Parameter(Mandatory=1)][string] $outputPath,
        [Parameter(Mandatory=1)][string] $collection,
        [Parameter(Mandatory=1)][string] $project,
        [Parameter(Mandatory=1)][string] $dropFolder,
        [Parameter(Mandatory=1)][string] $controller,
        [Parameter(Mandatory=0)][string] $vsVersion = "10.0"
    )

    $originalDir = Get-Location
    
    $moduleDir = $PSScriptRoot  
    
    $curDir = [System.IO.Path]::GetFullPath($moduleDir)

    if (!(Test-Path -Path $outputPath)) {
        Write-Error "Output path '$outputPath' does not exist."
        exit
    }

    $outBaseDir = Join-Path -Path $outputPath -ChildPath $project

    if ([String]::IsNullOrWhiteSpace($example) -eq $false) {
        $exampleDir = Join-Path -Path $curDir -ChildPath "..\Examples\$example"
    }
    elseif ([String]::IsNullOrWhiteSpace($exampleDir)) {
        Write-Error "Example or example directory must be specified."
        exit
    }

    if (!(Test-Path -Path $exampleDir)) {
        Write-Error "Example '$exampleDir' does not exist."
        exit
    }

    $name = Split-Path $exampleDir -Leaf

    try {
        Write-Host
        "Add Example Pipeline Utility"
        Write-Host
        "powerdelivery - http://github.com/eavonius/powerdelivery"
        Write-Host

        if ($(get-host).version.major -lt 3) {
            "Powershell 3.0 or greater is required."
            exit
        }

        LoadTFS -vsVersion $vsVersion    

        Remove-Item -Path $outBaseDir -Force -Recurse -ErrorAction SilentlyContinue | Out-Null

        mkdir -Force $outBaseDir | Out-Null
        cd $outputPath

        $computerName = $env:COMPUTERNAME

        "Creating TFS working folder for $project in $collection..."
        tf workfold /map "`$/$project" $outBaseDir /collection:"$collection" /workspace:"$computerName"

        "Getting files from project $project..."
        tf get "$project\*" /recursive /noprompt
        tf checkout /recursive "$project\*"

        "$exampleDir -> $outBaseDir"
        Copy-Item -Recurse -Path "$exampleDir\*" -Destination $outBaseDir -Force | Out-Null
        Copy-Item -Path (Join-Path -Path $curDir -ChildPath "BuildProcessTemplates") -Recurse -Destination "$outBaseDir" -Force

        $newScriptName = "$outBaseDir\$name.ps1"

        $buildDictionary = @{
            "$name - Local" = "Local";
            "$name - Commit" = "Commit";
            "$name - Test" = "Test";
            "$name - Capacity Test" = "CapacityTest";
            "$name - Production" = "Production";
        }
        
        "Checking in changed or new files to source control..."
        tf add "$project\*.*" /noprompt /recursive | Out-Null
        tf checkin "$project\*.*" /noprompt /recursive | Out-Null

        "Connecting to TFS server at $collection to create builds..."

        $projectCollection = [Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory]::GetTeamProjectCollection($collection)
        $buildServer = $projectCollection.GetService([Microsoft.TeamFoundation.Build.Client.IBuildServer])
        $structure = $projectCollection.GetService([Microsoft.TeamFoundation.Server.ICommonStructureService])

        $buildServerVersion = $buildServer.BuildServerVersion
                
        if ($buildServerVersion -eq 'v3') {
            $powerdelivery.tfsVersion = '2010'
        }
        elseif ($buildServerVersion -eq 'v4') {
            $powerdelivery.tfsVersion = '2012'
        }
        else {
            throw "TFS server must be version 2010 or 2012, a different version was detected."
        }

        $projectInfo = $structure.GetProjectFromName($project)
        if (!$projectInfo) {
            Write-Error "Project $project not found in TFS collection $collection"
            exit
        }

        $buildDictionary.GETENUMERATOR() | % {
            $buildName = $_.Key
            $buildEnv = $_.Value

            $build = $null

            if ($buildEnv -ne 'Local') {
                try {
                    $build = $buildServer.GetBuildDefinition($project, $buildName)
                    "Found build $buildName, updating..."
                }
                catch {
                    "Creating build $buildName..."
            
                    $build = $buildServer.CreateBuildDefinition($project)
                }
            
                $build.Name = $buildName

                if ($buildName.EndsWith("Commit")) {
                    $build.ContinuousIntegrationType = [Microsoft.TeamFoundation.Build.Client.ContinuousIntegrationType]::Individual
                }
                else {
                    $build.ContinuousIntegrationType = [Microsoft.TeamFoundation.Build.Client.ContinuousIntegrationType]::None
                }

                $build.BuildController = $buildServer.GetBuildController($controller)

                $buildFound = $false

                $processTemplatePath = "`$/$project/BuildProcessTemplates/PowerDeliveryTemplate.xaml"
                $changeSetTemplatePath = "`$/$project/BuildProcessTemplates/PowerDeliveryChangeSetTemplate.xaml"

                if ($powerdelivery.tfsVersion -eq 2012) {
                    $processTemplatePath = "`$/$project/BuildProcessTemplates/PowerDeliveryTemplate.11.xaml"
                    $changeSetTemplatePath = "`$/$project/BuildProcessTemplates/PowerDeliveryChangeSetTemplate.11.xaml"
                }

                $processTemplates = $buildServer.QueryProcessTemplates($project)

                foreach ($processTemplate in $processTemplates) {
                    if ($processTemplate.ServerPath -eq $processTemplatePath -and $buildEnv -eq "Commit") {
                        $build.Process = $processTemplate
                        $buildFound = $true
                        break
                    }
                    elseif ($processTemplate.ServerPath -eq $changeSetTemplatePath -and $buildEnv -ne "Commit") {
                        $build.Process = $processTemplate
                        $buildFound = $true
                        break
                    }
                }

                $build.DefaultDropLocation = $dropFolder

                if (!$buildFound) {

                    if ($buildEnv -eq "Commit") {
                        $templateToCreate = $processTemplatePath
                    }
                    else {
                        $templateToCreate = $changeSetTemplatePath
                    }

                    "Creating build process template for $templateToCreate..."
                    $processTemplate = $buildServer.CreateProcessTemplate($project, $templateToCreate)
                    $processTemplate.TemplateType = [Microsoft.TeamFoundation.Build.Client.ProcessTemplateType]::Custom
                    "Saving process template..."
                    $processTemplate.Save()
                    "Done"

                    if ($processTemplate -eq $null) {
                        throw "Couldn't find a process template at $templateToCreate"
                    }
                    $build.Process = $processTemplate
                }

                $processParams = [Microsoft.TeamFoundation.Build.Workflow.WorkflowHelpers]::DeserializeProcessParameters($build.ProcessParameters)
                $processParams["Environment"] = $buildEnv        
                $scriptPath = "`$/$project/$($name).ps1"
                $processParams["PowerShellScriptPath"] = $scriptPath
        
                $build.ProcessParameters = [Microsoft.TeamFoundation.Build.Workflow.WorkflowHelpers]::SerializeProcessParameters($processParams)

                $build.Save()
            }
        }

        $groupSecurity = $projectCollection.GetService([Microsoft.TeamFoundation.Server.IGroupSecurityService])
        $appGroups = $groupSecurity.ListApplicationGroups($projectInfo.Uri)

        $buildDictionary.Values | % {
            $envName = $_
            if ($envName -ne 'Commit' -and $envName -ne 'Local') {
                $groupName = "$name $envName Builders"
                $group = $null
                $appGroups | % {
                    if ($_.AccountName -eq $groupName) {
                        $group = $_
                    }
                }

                if (!$group) {
                    "Creating TFS security group $groupName..."
                    $groupSecurity.CreateApplicationGroup($projectInfo.Uri, $groupName, "Members of this group can queue $name builds targeting the $envName environment.") | Out-Null
                }
            }
        }
        #>
        Write-Host "Delivery pipeline '$name' ready at $collection for project '$project'" -ForegroundColor Green
    }
    finally {
        cd $originalDir
    }
}
tools\PowerDelivery\Add-Pipeline.ps1
<#
.Synopsis
Adds a powerdelivery build pipeline to a TFS project.

.Description
You can enable a Microsoft Team Foundation Server project to use powerdelivery in under a minute using this cmdlet. It allows you to select from one of several included templates as a starting point, and creates builds targeting each environment on Team Foundation Server with a powerdelivery PowerShell script for you to automate deployment. You can run this cmdlet multiple times specifying a different name each time to create a delivery pipeline for each software product you wish to deploy independently.

.Example
Add-Pipeline -name "MyApp" -collection "http://your-tfsserver/tfs" -project "My Project" -controller "MyController" -dropFolder "\\SERVER\share"

.Parameter name
The name of the product or component that will be delivered by this pipeline.

.Parameter collection
The URI of the TFS collection to add powerdelivery to.

.Parameter project
The TFS project to add powerdelivery to.

.Parameter dropFolder
The folder compiled assets should go into from the pipeline.

.Parameter controller
The name of the TFS build controller the pipeline should use.

.Parameter template
Optional. The name of a directory within the "Templates" directory of wherever you installed powerdelivery to (usually C:\Chocolatey\lib\powerdelivery<version>). The default is "Blank". A template minimally must have the following files:

Build.ps1
BuildLocal.yml
BuildCommit.yml
BuildTest.yml
BuildCapacityTest.yml
BuildProduction.yml
BuildShared.yml

Check out the templates page on the wiki (https://github.com/eavonius/powerdelivery/wiki/Templates) for more about which to use.
#>
function Add-Pipeline {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=1)][string] $name,
        [Parameter(Mandatory=1)][string] $collection,
        [Parameter(Mandatory=1)][string] $project,
        [Parameter(Mandatory=1)][string] $dropFolder,
        [Parameter(Mandatory=1)][string] $controller,
        [Parameter(Mandatory=0)][string] $template = "Blank",
        [Parameter(Mandatory=0)][string] $vsVersion = "10.0"
    )
	
	$originalDir = Get-Location
	
	$moduleDir = $PSScriptRoot	
	
	$curDir = [System.IO.Path]::GetFullPath($moduleDir)
	$buildsDir = Join-Path -Path $curDir -ChildPath "Pipelines"
	
	try {
	    Write-Host
	    "Add Pipeline Utility"
	    Write-Host
	    "powerdelivery - http://github.com/eavonius/powerdelivery"
	    Write-Host

	    if ($(get-host).version.major -lt 3) {
	        "Powershell 3.0 or greater is required."
	        exit
	    }

		LoadTFS -vsVersion $vsVersion

	    $outBaseDir = Join-Path -Path $buildsDir -ChildPath $project

        Remove-Item -Path $buildsDir -Force -Recurse -ErrorAction SilentlyContinue | Out-Null

	    mkdir -Force $outBaseDir | Out-Null
	    cd $buildsDir

	    "Removing existing workspace at $collection if it exists..."
	    tf workspace /delete "AddPowerDelivery_$($project)" /collection:"$collection" | Out-Null

	    if ($LASTEXITCODE -ne 0) {
	        Write-Host "NOTE: Error above is normal. This occurs if there wasn't a mapped working folder already."
	    }

        "Creating TFS workspace for $collection..."
        tf workspace /new /noprompt "AddPowerDelivery_$($project)" /collection:"$collection"

        "Getting files from project $project..."
        tf get "$project\*" /recursive /noprompt

        $templateDir = Join-Path -Path $curDir -ChildPath "Templates\$template"

        if (!(Test-Path -Path $templateDir)) {
            Write-Error "Template '$template' does not exist."
            exit
        }

        "$templateDir -> $outBaseDir"
        Copy-Item -Recurse -Path "$templateDir\*" -Destination $outBaseDir -Force | Out-Null
        Copy-Item -Path (Join-Path -Path $curDir -ChildPath "BuildProcessTemplates") -Recurse -Destination "$outBaseDir" -Force

        $newScriptName = "$outBaseDir\$name.ps1"

        Move-Item -Force "$outBaseDir\Build.ps1" "$newScriptName"
		Move-Item -Force "$outBaseDir\BuildShared.yml" "$outBaseDir\$($name)Shared.yml"

		$buildDictionary = @{
            "$name - Local" = "Local";
            "$name - Commit" = "Commit";
            "$name - Test" = "Test";
            "$name - Capacity Test" = "CapacityTest";
            "$name - Production" = "Production";
        }
		
		$buildDictionary.Values | % {
			$envName = $_
			$sourcePath = "$outBaseDir\Build$($envName).yml"
			$destPath = "$outBaseDir\$($name)$($envName).yml"
			if (Test-Path $sourcePath) {
				Move-Item -Force $sourcePath $destPath
			}
		}

        "Replacing build template variables..."
        (Get-Content "$newScriptName") | % {
            $_ -replace '%BUILD_NAME%', $name
        } | Set-Content "$newScriptName"

        "Checking in changed or new files to source control..."
        tf add "$project\*.*" /noprompt /recursive | Out-Null
        tf checkin "$project\*.*" /noprompt /recursive | Out-Null

        "Connecting to TFS server at $collection to create builds..."

        $projectCollection = [Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory]::GetTeamProjectCollection($collection)
        $buildServer = $projectCollection.GetService([Microsoft.TeamFoundation.Build.Client.IBuildServer])
        $structure = $projectCollection.GetService([Microsoft.TeamFoundation.Server.ICommonStructureService])

		$buildServerVersion = $buildServer.BuildServerVersion
				
		if ($buildServerVersion -eq 'v3') {
			$powerdelivery.tfsVersion = '2010'
		}
		elseif ($buildServerVersion -eq 'v4') {
			$powerdelivery.tfsVersion = '2012'
		}
		else {
			throw "TFS server must be version 2010 or 2012, a different version was detected."
		}

        $projectInfo = $structure.GetProjectFromName($project)
        if (!$projectInfo) {
            Write-Error "Project $project not found in TFS collection $collection"
            exit
        }

        $buildDictionary.GETENUMERATOR() | % {
            $buildName = $_.Key
            $buildEnv = $_.Value

            $build = $null

            if ($buildEnv -ne 'Local') {
                try {
                    $build = $buildServer.GetBuildDefinition($project, $buildName)
                    "Found build $buildName, updating..."
                }
                catch {
                    "Creating build $buildName..."
            
                    $build = $buildServer.CreateBuildDefinition($project)
                }
            
                $build.Name = $buildName

                if ($buildName.EndsWith("Commit")) {
                    $build.ContinuousIntegrationType = [Microsoft.TeamFoundation.Build.Client.ContinuousIntegrationType]::Individual
                }
                else {
                    $build.ContinuousIntegrationType = [Microsoft.TeamFoundation.Build.Client.ContinuousIntegrationType]::None
                }

                $build.BuildController = $buildServer.GetBuildController($controller)

                $buildFound = $false

		        $processTemplatePath = "`$/$project/BuildProcessTemplates/PowerDeliveryTemplate.xaml"
                $changeSetTemplatePath = "`$/$project/BuildProcessTemplates/PowerDeliveryChangeSetTemplate.xaml"

		        if ($powerdelivery.tfsVersion -eq 2012) {
        	        $processTemplatePath = "`$/$project/BuildProcessTemplates/PowerDeliveryTemplate.11.xaml"
			        $changeSetTemplatePath = "`$/$project/BuildProcessTemplates/PowerDeliveryChangeSetTemplate.11.xaml"
		        }

                $processTemplates = $buildServer.QueryProcessTemplates($project)

                foreach ($processTemplate in $processTemplates) {
                    if ($processTemplate.ServerPath -eq $processTemplatePath -and $buildEnv -eq "Commit") {
                        $build.Process = $processTemplate
                        $buildFound = $true
                        break
                    }
                    elseif ($processTemplate.ServerPath -eq $changeSetTemplatePath -and $buildEnv -ne "Commit") {
                        $build.Process = $processTemplate
                        $buildFound = $true
                        break
                    }
                }

                $build.DefaultDropLocation = $dropFolder

                if (!$buildFound) {

                    if ($buildEnv -eq "Commit") {
                        $templateToCreate = $processTemplatePath
                    }
                    else {
                        $templateToCreate = $changeSetTemplatePath
                    }

                    "Creating build process template for $templateToCreate..."
                    $processTemplate = $buildServer.CreateProcessTemplate($project, $templateToCreate)
                    $processTemplate.TemplateType = [Microsoft.TeamFoundation.Build.Client.ProcessTemplateType]::Custom
                    "Saving process template..."
                    $processTemplate.Save()
                    "Done"

                    if ($processTemplate -eq $null) {
                        throw "Couldn't find a process template at $templateToCreate"
                    }
                    $build.Process = $processTemplate
                }

                $processParams = [Microsoft.TeamFoundation.Build.Workflow.WorkflowHelpers]::DeserializeProcessParameters($build.ProcessParameters)
                $processParams["Environment"] = $buildEnv        
                $scriptPath = "`$/$project/$($name).ps1"
                $processParams["PowerShellScriptPath"] = $scriptPath
        
                $build.ProcessParameters = [Microsoft.TeamFoundation.Build.Workflow.WorkflowHelpers]::SerializeProcessParameters($processParams)

                $build.Save()
            }
        }

        $groupSecurity = $projectCollection.GetService([Microsoft.TeamFoundation.Server.IGroupSecurityService])
        $appGroups = $groupSecurity.ListApplicationGroups($projectInfo.Uri)

        $buildDictionary.Values | % {
            $envName = $_
            if ($envName -ne 'Commit' -and $envName -ne 'Local') {
                $groupName = "$name $envName Builders"
                $group = $null
                $appGroups | % {
                    if ($_.AccountName -eq $groupName) {
                        $group = $_
                    }
                }

                if (!$group) {
                    "Creating TFS security group $groupName..."
                    $groupSecurity.CreateApplicationGroup($projectInfo.Uri, $groupName, "Members of this group can queue $name builds targeting the $envName environment.") | Out-Null
                }
            }
        }

        Write-Host "Delivery pipeline '$name' ready at $collection for project '$project'" -ForegroundColor Green
    }
    finally {
		try {
        	tf workspace /delete "AddPowerDelivery_$($project)" /collection:"$collection" | Out-Null
		}
		catch {}
        del -Path $buildsDir -Force -Recurse -ErrorAction SilentlyContinue | Out-Null
		cd $originalDir
    }
}
tools\PowerDelivery\Add-RemoteCredSSPTrustedHost.ps1
function Add-RemoteCredSSPTrustedHost {
    param(
        [Parameter(Position=0,Mandatory=1)] [string] $clientComputerName,
        [Parameter(Position=1,Mandatory=1)] [string] $serverComputerName
    )

    $logPrefix = "Add-RemoteCredSSPTrustedHost"

    Invoke-Command -ComputerName $serverComputerName -ArgumentList @($logPrefix) -ScriptBlock {
        param($varLogPrefix)
        Write-Host "$varLogPrefix Enabling $($env:COMPUTERNAME) to receive remote CredSSP credentials"
        Enable-WSManCredSSP -Role Server -Force | Out-Null
    }

    Invoke-Command -ComputerName $clientComputerName `
        -ArgumentList @($serverComputerName, $logPrefix) `
        -ScriptBlock { 
            param($varServerComputerName, $varLogPrefix)

            $credSSP = Get-WSManCredSSP

            $computerExists = $false

            if ($credSSP -ne $null) {
                if ($credSSP.length -gt 0) {
                    $trustedClients = $credSSP[0].Substring($credSSP[0].IndexOf(":") + 2)
                    $trustedClientsList = $trustedClients -split "," | % { $_.Trim() }
            
                    if ($trustedClientsList.Contains("wsman/$varServerComputerName")) {
                        $computerExists = $true
                    }
                }
            }

            if (!$computerExists) {
                Write-Host "$varLogPrefix Enabling CredSSP credentials to travel from $($env:COMPUTERNAME) to $varServerComputerName"
                Enable-WSManCredSSP -Role Client -DelegateComputer "$varServerComputerName" -Force | Out-Null
            }
        }
}
tools\PowerDelivery\Add-WindowsUserToGroup.ps1
<#
.Synopsis
Adds an existing Windows user account on a computer to a specific Windows security group.

.Description
Adds an existing Windows user account on a computer to a specific Windows security group.

.Parameter userName
The username of the account to add to the group.

.Parameter groupName
The name of the group to add the user to.

.Parameter computerName
The computer to be modified.

.Example
Add-WindowsUserToGroup -userName 'DOMAIN\MyUser' `
					   -groupName 'Performance Monitor Users' `
					   -computerName MYCOMPUTER
#>
function Add-WindowsUserToGroup {
	[CmdletBinding()]
	param(
		[Parameter(Position=0,Mandatory=1)] $userName,
		[Parameter(Position=1,Mandatory=1)] $groupName,
		[Parameter(Position=2,Mandatory=1)] $computerName
	)
	
	Set-Location $powerdelivery.deployDir

    $logPrefix = "Add-WindowsUserToGroup:"

    $computerNames = $computerName -split "," | % { $_.Trim() }

    foreach ($curComputerName in $computerNames) {

	    Invoke-Command $curComputerName {

		    $group = [ADSI]"WinNT://$using:curComputerName/$using:groupName,group"
		    $usersSet = [ADSI]"WinNT://$using:curComputerName/$using:groupName"
		    $users = @($usersSet.psbase.Invoke("Members")) 

		    $foundAccount = $false

		    $users | foreach {
			    if ($_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null) -eq $using:userName) {
				    $foundAccount = $true
			    }
		    }

		    if (!$foundAccount) {
			    "$using:logPrefix Adding $using:userName user to $using:groupName group on $($using:curComputerName)..."

			    $group.psbase.Invoke("Add", ([ADSI]"WinNT://$using:userName").path)

			    "$using:logPrefix User $using:userName added to $using:groupName group on $($using:curComputerName) successfully."
		    }
	    }
    }
}
tools\PowerDelivery\Backup-MasterDataServices.ps1
function Backup-MasterDataServices {
    param(
        [Parameter(Position=0,Mandatory=1)][string] $computerName,
        [Parameter(Position=1,Mandatory=1)][string] $model,
        [Parameter(Position=2,Mandatory=1)][string] $service,
        [Parameter(Position=3,Mandatory=1)][string] $package,
        [Parameter(Position=4,Mandatory=0)][string] $version = "VERSION_1",
        [Parameter(Position=5,Mandatory=0)][switch] $includeData = $false,
        [Parameter(Position=6,Mandatory=0)][string] $credentialUserName,
        [Parameter(Position=7,Mandatory=0)][string] $mdsDeployPath = "C:\Program Files\Microsoft SQL Server\110\Master Data Services\Configuration\"
    )
    
    $logPrefix = "Backup-MasterDataServices:"

    $computerNames = $computerName -split "," | % { $_.Trim() }

    foreach ($curComputerName in $computerNames) {

        "$logPrefix Backing up Master Data Services model $model on $computerName"

        $dropLocation = Get-BuildDropLocation

        # Allow credentials to travel from remote computer to TFS server
        #
        $dropUri = New-Object -TypeName System.Uri -ArgumentList $dropLocation
        
        if ($dropUri.IsUnc) {
            
            $dropHost = $dropUri.Host
            
            $remoteComputer = [System.Net.Dns]::GetHostByName("$dropHost").HostName

            Add-RemoteCredSSPTrustedHost $curComputerName $remoteComputer
        }

        $invokeArgs = @{
            "ArgumentList" = @($model, $service, $package, $version, $includeData, $mdsDeployPath, $dropLocation, $logPrefix);
            "ScriptBlock" = {
                param($varModel, $varService, $varPackage, $varVersion, $varIncludeData, $varMdsDeployPath, $varDropLocation, $varLogPrefix)

                $tempOutputDirectory = Join-Path $env:TEMP "PowerDelivery"
                
                # Create the subdirectory of the package path if one was specified
                #
                $packagePath = [System.IO.Path]::GetDirectoryName("$varPackage")
                if ($packagePath) {
                    $tempSubPath = Join-Path "$tempOutputDirectory" "$packagePath"
                    if (!(Test-Path -Path $tempSubPath)) {
                        New-Item -ItemType Directory -Path "$tempSubPath" | Out-Null
                    }
                }
                
                $tempPackageFile = Join-Path "$tempOutputDirectory" "$varPackage"

                # Delete the prior temporary backup if one exists
                #
                if (Test-Path -Path "$tempPackageFile") {
                    Remove-Item -Path "$tempPackageFile" -Force | Out-Null
                }

                # NOTE: The TFS Build Service account must have been given function and 
                # model permission to MDS so this command will succeed.
                #
                $mdsDeployPath = Join-Path $varMdsDeployPath "MDSModelDeploy"

                $mdsDeployCommand = """$mdsDeployPath"" createpackage -package ""$tempPackageFile"" -model ""$varModel"" -service ""$varService"""

                if ($varIncludeData) {
                    $mdsDeployCommand += " -version ""$varVersion"" -includedata"
                }

                Write-Host "$varLogPrefix $mdsDeployCommand"
                Invoke-Expression "& $mdsDeployCommand"

                # Copy the Master Data Services deployment package from the temporary directory to the build drop location.
                #
                if (Test-Path $tempPackageFile) {
                    $destPackagePath = $varDropLocation
                    if ($packagePath) {
                        $dropPath = Join-Path "$varDropLocation" "$packagePath"
                        if (!(Test-Path -Path "$dropPath")) {
                            New-Item -ItemType Directory -Path "$dropPath" | Out-Null
                        }
                        $destPackagePath = $dropPath
                    }
                    Write-Host "$varLogPrefix $tempPackageFile -> $destPackagePath"
                    Copy-Item "$tempPackageFile" "$destPackagePath"
                }

                if ($LASTEXITCODE -ne $null -and $LASTEXITCODE -ne 0) {
                    throw "Error backing up Master Data Services, exit code was $LASTEXITCODE"
                }
            };
            "ErrorAction" = "Stop"
        }

        Add-CommandCredSSP $curComputerName $invokeArgs $credentialUserName

        Invoke-Command @invokeArgs

        Write-BuildSummaryMessage -name "Backup" -header "Backups" -message "Master Data Services: $computerName -> $package"
    }
}
tools\PowerDelivery\BuildProcessTemplates\PowerDeliveryChangeSetTemplate.11.xaml
 
tools\PowerDelivery\BuildProcessTemplates\PowerDeliveryChangeSetTemplate.xaml
 
tools\PowerDelivery\BuildProcessTemplates\PowerDeliveryTemplate.11.xaml
 
tools\PowerDelivery\BuildProcessTemplates\PowerDeliveryTemplate.xaml
 
tools\PowerDelivery\Copy-Robust.ps1
function Copy-Robust {
    [CmdletBinding()]
    param(
        [Parameter(Position=0,Mandatory=1)][string] $path,
	    [Parameter(Position=1,Mandatory=1)][string] $destination,
	    [Parameter(Position=2,Mandatory=0)][string] $filter	= "*.*",
        [Parameter(Position=3,Mandatory=0)][switch] $recurse = $false,
        [Parameter(Position=4,Mandatory=0)][switch] $excludeNewer = $false,
        [Parameter(Position=5,Mandatory=0)][switch] $excludeOlder = $true
    )

    $logPrefix = "Copy-Robust:"

    mkdir -Force -ErrorAction SilentlyContinue $destination | Out-Null

    $command = "robocopy `"$path`" `"$destination`" $filter /E /NP /ETA /NJH /NJS /NFL /NDL"

    if ($excludeNewer) {
        $command += " /XN"
    }

    if ($excludeOlder) {
        $command += " /XO"
    }

    if ($recurse -eq $false) {
        $command += " /LEV:1"
    }

    "$logPrefix $command"
    Invoke-Expression $command
            
    if ($LASTEXITCODE -ge 8) {
        throw "Robocopy failed to copy one or more files."
    }
}
tools\PowerDelivery\Deploy-BuildAssets.ps1
function Deploy-BuildAssets {
	[CmdletBinding()]
	param(
		[Parameter(Position=0,Mandatory=1)][string] $computerName,
		[Parameter(Position=1,Mandatory=1)][string] $path,
		[Parameter(Position=2,Mandatory=1)][string] $destination,
		[Parameter(Position=3,Mandatory=0)][string] $filter	= "*.*",
		[Parameter(Position=4,Mandatory=0)][switch] $recurse = $false,
		[Parameter(Position=5,Mandatory=0)][string] $driveLetter = $powerdelivery.deployDriveLetter
	)
	
	$logPrefix = "Deploy-BuildAssets:"
	
	$computerNames = $ComputerName -split "," | % { $_.Trim() }
	
	$dropLocation = Get-BuildDropLocation
	$dropSource = Join-Path $powerdelivery.deployDir $path
	
	foreach ($curComputerName in $computerNames) {
	
		$remoteDeployPath = Get-ComputerRemoteDeployPath $curComputerName $driveLetter
        $remoteDestinationPath = Join-Path $remoteDeployPath $destination
		
		mkdir -Force $remoteDestinationPath | Out-Null
		
		Copy-Robust $dropSource $remoteDestinationPath -filter $filter -recurse:$recurse.IsPresent

        Write-BuildSummaryMessage -name "Deploy" -header "Deployments" -message "Build Assets: $path -> $destination ($computerName)"
	}
}
tools\PowerDelivery\Disable-SqlJobs.ps1
<#
.Synopsis
Disables SQL jobs on a Microsoft SQL database server

.Description
Disables SQL jobs on a Microsoft SQL database server. You can disable a set of jobs that matches a wildcard. 
If any of the jobs matching a wildcard are already running, the previously disabled jobs that match the same 
wildcard will be restarted and an error will occur. This is to make sure all the jobs disable successfully 
together.

.Parameter serverName
The SQL server instance on which the jobs will be disabled.

.Parameter jobs
The jobs to disable. Can be a single job name, or a name with wildcards.

.Example
Disable-SqlJobs -serverName localhost -jobs MyJobs*
#>
function Disable-SqlJobs {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=1)][string] $serverName, 
        [Parameter(Mandatory=1)][string] $jobs
    )

    $logPrefix = "Disable-SqlJobs:"

	[Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | Out-Null

    Write-Host "$logPrefix Disabling SQL jobs with pattern $jobs on $serverName"

    $dataMartServer = New-Object Microsoft.SqlServer.Management.SMO.Server("$serverName")
    $dataMartJobs = $dataMartServer.jobserver.jobs | where-object {$_.Isenabled -eq $true -and  $_.name -like "$jobs"}

    $jobRunning = $false
    $jobName = ''

    foreach ($dataMartJob in $dataMartJobs)	{	
        $jobName = $dataMartJob.Name
        if ($dataMartJob.CurrentRunStatus.ToString() -ne 'Idle') {
            $jobRunning = $true
            break
        }	
        else {	
            $dataMartJob.IsEnabled = $false
            $dataMartJob.Alter()
            Write-Host "$logPrefix Job '$jobName' successfully disabled."
        }
    }

    if ($jobRunning) {
        foreach ($dataMartJob in $dataMartJobs)	{	
            $dataMartJob.IsEnabled = $true
            $dataMartJob.Alter()
        }
        throw "$logPrefix Job '$jobName' is still running, stopping build."
    }
}
tools\PowerDelivery\Enable-SqlJobs.ps1
<#
.Synopsis
Enables SQL jobs on a Microsoft SQL database server

.Description
Enables SQL jobs on a Microsoft SQL database server. You can enable a set of jobs that matches a wildcard.

.Parameter serverName
The SQL server instance on which the jobs will be enabled.

.Parameter jobs
The jobs to enable. Can be a single job name, or a name with wildcards.

.Example
Enable-SqlJobs -serverName localhost -jobs MyJobs*
#>
function Enable-SqlJobs {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=1)][string] $serverName, 
        [Parameter(Mandatory=1)][string] $jobs
    )

    $logPrefix = "Enable-SqlJobs:"
	
	[Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | Out-Null

    Write-Host "$logPrefix Enabling SQL jobs with pattern $jobs on $serverName"

    $dataMartServer = New-Object Microsoft.SqlServer.Management.SMO.Server("$serverName")
    $dataMartJobs = $dataMartServer.jobserver.jobs | where-object {$_.name -like "$jobs"}
    foreach ($dataMartJob in $dataMartJobs)	{	
        $jobName = $dataMartJob.Name
        $dataMartJob.IsEnabled = $true
        $dataMartJob.Alter()
        Write-Host "$logPrefix Job '$jobName' successfully enabled."
    }
}
tools\PowerDelivery\Enable-WebDeploy.ps1
<#
.Synopsis
Sets up an IIS server to allow web deployment via msdeploy.exe.

.Description
The Enable-WebDeploy cmdlet is used to configure an IIS website for deployment.

.Example
Enable-WebDeploy -webComputer 'MyWebServer' -webDeployDir 'C:\Program Files\Microsoft Web Deploy v3' -webSite 'MySite' -webPort '8080' -webPassword '3F#g&jKl'

.Parameter webComputer
The name of the computer to enable web deployment for. Must be Windows Server running IIS 7 or greater, with Web Deploy 3.0 and "Recommended Host Configuration" setup using Microsoft Platform Installer.

.Parameter webDeployDir
The directory on the web server computer into which Web Deploy 3 is installed. You can use a remote powershell command to read this out of the registry of the remote computer if your different enviroments have installed it in different locations.

.Parameter webSite
The name of the website to create. A corresponding application pool with the same name will also be created.

.Parameter webPort
The port the website should run on. Must not be an existing port in use on the server.

.Parameter webPassword
A user account will be created on the server that will allow deployment to it named after the website. This paramter specifies the password of that account.

.Parameter runtimeVersion
Optional. The version of .NET the application pool should be created with. Defaults to '4.0'.
#>
function Enable-WebDeploy {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=1)] $webComputer, 
        [Parameter(Mandatory=1)][string] $webDeployDir, 
        [Parameter(Mandatory=1)][string] $webSite, 
        [Parameter(Mandatory=1)][string] $webPort, 
        [Parameter(Mandatory=1)][string] $webPassword, 
        [Parameter(Mandatory=0)][string] $runtimeVersion = 'v4.0'
    )
	
	Set-Location $powerdelivery.deployDir

    $logPrefix = "Publish-WebDeploy:"

    $computerNames = Get-ArrayFromStringOrHash $webComputer
	
    foreach ($curComputerName in $computerNames) {

	    if ($curComputerName -ne 'localhost' -and $powerdelivery.environment -ne 'Local') {

            $remoteWebDeployDir = Invoke-Command -ComputerName $curComputerName {

                $msDeploy3Path = Get-ItemProperty -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\IIS Extensions\MSDeploy\3" -Name InstallPath -ErrorAction SilentlyContinue

                if (![String]::IsNullOrWhiteSpace($msDeploy3Path)) {
                    $msDeploy3Path.InstallPath
                }
                else {
                    throw "Couldn't find web deploy 3.0 on $($using:curComputerName). Please install the Web Platform Installer with Web Deploy 3.0 to continue."
                }
            }

    	    $webDeployScriptsDir = Join-Path $remoteWebDeployDir "Scripts"

            $siteSetupArgs = @(
                "-siteName `"$webSite`"",
                "-publishSettingSavePath `"C:\Inetpub\$webSite`"",
                "-publishSettingFileName `"$($webSite).publishsettings`"",
                "-sitePhysicalPath `"C:\Inetpub\$webSite`"",
                "-sitePort $webPort",
                "-siteAppPoolName `"$webSite`"",
                "-deploymentUserName `"$webSite`"",
                "-deploymentUserPassword '$webPassword'",
                "-managedRunTimeVersion `"$runtimeVersion`""
            )

            Invoke-Command -ComputerName $curComputerName { 
                $setupScriptName = "SetupSiteForPublish.ps1"
                $setupScriptPath = Join-Path $using:webDeployScriptsDir $setupScriptName

                "$using:logPrefix `"$setupScriptPath`" $using:siteSetupArgs"

                Invoke-Expression "& `"$setupScriptPath`" $using:siteSetupArgs" | Out-Host
            }
	    }
    }
}
tools\PowerDelivery\Exec.ps1
<#
.Synopsis
Executes a command line executable. Original source included in psake (https://github.com/psake/psake).

.Description
Executes a command line executable. Throws an exception if a non-zero exit code is encountered.

.Parameter cmd
The command to execute.

.Parameter errorMessage
Optional. The error message to return in the exception if you wish to override the default.

.Example
Exec -errorMessage "Failed to compile MyProject" {
	msbuild.exe MyProject.proj /v:m
}
#>
function Exec {
    [CmdletBinding()]
    param(
        [Parameter(Position=0,Mandatory=1)][scriptblock]$cmd,
        [Parameter(Position=1,Mandatory=0)][string]$errorMessage = ("Error executing command {0}" -f $cmd)
    )
    & $cmd
    if ($lastexitcode -ne 0) {
        throw ("Exec: " + $errorMessage)
    }
}
tools\PowerDelivery\Export-BuildCredentials.ps1
function Export-BuildCredentials {

	Set-Location $powerdelivery.deployDir

    $currentDirectory = Get-Location
    $credentialsPath = Join-Path $currentDirectory Credentials

    if (!(Test-Path $credentialsPath)) {
        mkdir -Force $credentialsPath | Out-Null
    }

    $keyBytes = @()

    $credentialsKeyPath = Join-Path $credentialsPath "Credentials.key"
    if (!(Test-Path $credentialsKeyPath)) {
        $keyBytes = (1..32 | % { [byte](Get-Random -Minimum 0 -Maximum 255) })
        [IO.File]::WriteAllBytes($credentialsKeyPath, $keyBytes)
    }
    else {
        $keyBytes = Get-Content $credentialsKeyPath
    }

    "Enter the username of an account to export credentials of:"
    $userName = Read-Host

    $userNameFile = $userName -replace "\\", "#"

    $userNamePath = Join-Path $credentialsPath "$($userNameFile).txt"

    "Enter the password of the account:"
    $password = Read-Host -AsSecureString | ConvertFrom-SecureString -Key $keyBytes | Out-File $userNamePath -Force

    "Credentials exported at $userNamePath"
}
tools\PowerDelivery\Get-ArrayFromStringOrHash.ps1
function Get-ArrayFromStringOrHash {
    param(
        [Parameter(Position=0,Mandatory=1)] $computerNames
    )
    
    if ($computerNames.GetType().Name -eq 'Hashtable') {
        return $computerNames.Values
    }
    else {
        return @($computerNames)
    }
}
tools\PowerDelivery\Get-BuildAppVersion.ps1
<#
.Synopsis
Gets the application build version.

.Description
Returns the version of the application. Should be used to version assets so they match the build changeset.

.Outputs
The version of the application with the 4th segment being the TFS changeset number.

.Example
$appVersion = Get-BuildAppversion
#>
function Get-BuildAppVersion {
    [CmdletBinding()]
    param()
    return $powerdelivery.buildAppVersion;
}
tools\PowerDelivery\Get-BuildAssemblyVersion.ps1
<#
.Synopsis
Gets the assembly build version.

.Description
Returns the version of the application. Should be used to version assemblies so they match the build version.

.Outputs
The version of the application as specified when declaring the Pipeline at the top of your delivery pipeline script.

.Example
$assemblyVersion = Get-BuildAssemblyVersion
#>
function Get-BuildAssemblyVersion {
    [CmdletBinding()]
    param()
    return $powerdelivery.buildAssemblyVersion;
}
tools\PowerDelivery\Get-BuildAssets.ps1
<#
.Synopsis
Copies build assets from the drop location to the build working directory.

.Description
Copies build assets from the drop location to the build working directory. You should specify 
relative paths for this command.

.Parameter path
The relative remote path of assets at the drop location that should be copied locally.

.Parameter destination
The relative local path to copy the assets to.

.Parameter filter
Optional. A filter for the file extensions that should be included.

.Parameter recurse
Optional. Set to recursively copy all files within the source to the destination.

.Example
Get-BuildAssets "SomeDir\SomeFiles" "SomeDir" -Filter *.*
#>
function Get-BuildAssets {
	[CmdletBinding()]
	param(
		[Parameter(Position=0,Mandatory=1)][string] $path,
		[Parameter(Position=1,Mandatory=1)][string] $destination,
		[Parameter(Position=2,Mandatory=0)][string] $filter	= $null,
		[Parameter(Position=3,Mandatory=0)][switch] $recurse = $false
	)

	$currentDirectory = Get-Location
	$dropLocation = Get-BuildDropLocation
	
	$sourcePath = Join-Path $dropLocation $path
	$destinationPath = Join-Path $currentDirectory $destination
	
	mkdir -Force $destinationPath | Out-Null

	$copyArgs = @{"Force" = $true; "Filter" = $filter; "Path" = $sourcePath; "Destination" = $destinationPath}

	if ($recurse) {
		$copyArgs.Add("Recurse", $true)
	}

	& copy @copyArgs
}
tools\PowerDelivery\Get-BuildChangeSet.ps1
<#
.Synopsis
Gets the TFS changeset of the source code being built.

.Description
Returns the changeset of the build.

.Outputs
The changeset of the TFS checkin being built (for example, C45 for changeset 46).

.Example
$changeSet = Get-BuildChangeSet
#>
function Get-BuildChangeSet {
    [CmdletBinding()]
    param()
    return $powerdelivery.changeSet
}
tools\PowerDelivery\Get-BuildCollectionUri.ps1
<#
.Synopsis
Gets the Uri of the TFS project collection containing the project being built.

.Description
Gets the Uri of the TFS project collection containing the project being built.

.Outputs
The Uri of the TFS project collection containing the project being built.

.Example
$collectionUri = Get-BuildCollectionUri
#>
function Get-BuildCollectionUri {
    [CmdletBinding()]
    param()
    return $powerdelivery.collectionUri
}
tools\PowerDelivery\Get-BuildConfig.ps1
<#
.Synopsis
Gets YAML configuration used for the build. The values are in a hash that contains the result of 
merging the target environment (Commit, Test, or Production etc.) and any entries in the "Shared" 
configuration. Entries in the target environment overwrite the shared one.

.Description
Gets YAML configuration used for the build. The values are in a hash that contains the result of 
merging the target environment (Commit, Test, or Production etc.) and any entries in the "Shared" 
configuration. Entries in the target environment overwrite the shared one.

.Outputs
The YAML configuration object containing build configuration settings.

.Example
$buildConfig = Get-BuildConfig
#>
function Get-BuildConfig {
	[CmdletBinding()]
	param()
	return $powerdelivery.config
}
tools\PowerDelivery\Get-BuildCredentials.ps1
function Get-BuildCredentials {
    param(
        [Parameter(Position=0,Mandatory=0)] $userName
    )

    $credentialUserName = $userName

    if ([String]::IsNullOrWhiteSpace("$credentialUserName")) {
        $credentialUserName = whoami
    }

    if (!$powerdelivery.buildCredentials.ContainsKey($credentialUserName)) {

        $credentials = $null

        if ($powerdelivery.environment -ne 'Local') {

            $currentDirectory = Get-Location
            $credentialsPath = Join-Path $currentDirectory Credentials

            if (!(Test-Path $credentialsPath)) {
                throw "Path $credentialsPath containing build credentials does not exist."
            }

            [byte[]]$keyBytes = New-Object byte[] 0

            $credentialsKeyPath = Join-Path $credentialsPath "Credentials.key"
            if (!(Test-Path $credentialsKeyPath)) {
                throw "Credentials keyfile is missing."
            }
            else {
                try {
                    [System.IO.Stream]$stream = [System.IO.File]::OpenRead($credentialsKeyPath)
                    try {
                        $keyBytes = New-Object byte[] $stream.length
                        [void] $stream.Read($keyBytes, 0, $stream.Length)
                    }
                    finally {
                        $stream.Close() | Out-Null
                    }
                } 
                catch {
                    throw "Error reading file $credentialsKeyPath - $_"
                }
            }

            $userNameFile = $credentialUserName -replace "\\", "#"
            $userNamePath = Join-Path $credentialsPath "$($userNameFile).txt"

            if (!(Test-Path $userNamePath)) {
                throw "File $userNamePath does not exist. Did you run Export-BuildCredentials to store them first?"
            }

            $password = Get-Content $userNamePath | ConvertTo-SecureString -Key $keyBytes

            $credentials = new-object -typename System.Management.Automation.PSCredential -argumentlist $credentialUserName, $password
        }
        else {
            $credentials = Get-Credential -UserName $credentialUserName
        }

        $powerdelivery.buildCredentials.Add($credentialUserName, $credentials)
        $credentials
    }
    else {
        $powerdelivery.buildCredentials[$credentialUserName]
    }
}
tools\PowerDelivery\Get-BuildDropLocation.ps1
<#
.Synopsis
Gets the remote UNC path into which build assets should be placed.

.Description
Gets the remote UNC path into which build assets should be placed. You can use the 
Publish-BuildAssets and Get-BuildAssets functions to push and pull files between 
the local directory and the one returned by calling this function.

.Outputs
The remote UNC path into which build assets should be placed.

.Example
$dropLocation = Get-BuildDropLocation
#>
function Get-BuildDropLocation {
    [CmdletBinding()]
    param()
    return $powerdelivery.dropLocation
}
tools\PowerDelivery\Get-BuildEnvironment.ps1
<#
.Synopsis
Gets the environment the currently executing build is targeting for deployment.

.Description
Gets the environment the currently executing build is targeting. Should be 
"Local", "Commit", "Test", "CapacityTest", or "Production".

.Outputs
The environment the currently executing build is targeting for deployment.

.Example
$environment = Get-BuildEnvironment
#>
function Get-BuildEnvironment {
    [CmdletBinding()]
    param()
    return $powerdelivery.environment
}
tools\PowerDelivery\Get-BuildName.ps1
<#
.Synopsis
Gets the name of the currently executing build.

.Description
Gets the name of the currently executing build. This value will 
match the name of the build as viewed in TFS and must be used to 
call some TFS APIs.

.Outputs
The name of the currently executing build.

.Example
$name = Get-BuildName
#>
function Get-BuildName {
    [CmdletBinding()]
    param()
    return $powerdelivery.buildName
}
tools\PowerDelivery\Get-BuildNumber.ps1
<#
.Synopsis
Gets the number of the currently executing build.

.Description
Gets the number of the currently executing build. This value 
must be used to call some TFS APIs.

.Outputs
The number of the currently executing build.

.Example
$number = Get-BuildNumber
#>
function Get-BuildNumber {
    [CmdletBinding()]
    param()
    return $powerdelivery.buildNumber
}
tools\PowerDelivery\Get-BuildOnServer.ps1
<#
.Synopsis
Gets whether the build is running on the TFS server.

.Description
Returns whether the build is executing on the build server or not.

.Outputs
Whether the build is executing on the build server or not.

.Example
$onServer = Get-BuildOnServer
#>
function Get-BuildOnServer {
    [CmdletBinding()]
    param()
    return $powerdelivery.onServer
}
tools\PowerDelivery\Get-BuildRequestedBy.ps1
<#
.Synopsis
Gets the account name of the user who requested the build.

.Description
Gets the account name of the user who requested the build.

.Outputs
The account name of the user who requested the build.

.Example
$requestedBy = Get-BuildRequestedBy
#>
function Get-BuildRequestedBy {
    [CmdletBinding()]
    param()
    return $powerdelivery.requestedBy
}
tools\PowerDelivery\Get-BuildSetting.ps1
<#
.Synopsis
Gets a configuration setting from the YML files for the environment 
the currently executing build is targeting for deployment.

.Description
Gets a configuration setting from the YML files for the environment 
the currently executing build is targeting for deployment.

.Parameter name
The name of the setting from the YML file to get.

.Outputs
The value of the setting from the YML file for the setting that was 
requested.

.Example
$webServerName = Get-BuildSetting WebServerName
#>
function Get-BuildSetting {
    [CmdletBinding()]
    param([Parameter(Position=0,Mandatory=1)][string] $name)

	if (!$powerdelivery.config.ContainsKey($name)) {
		throw "Couldn't find build setting '$name'"
	}

	$powerdelivery.config[$name]
}
tools\PowerDelivery\Get-BuildTeamProject.ps1
<#
.Synopsis
Gets the name of the TFS project the build is delivering assets for.

.Description
Gets the name of the TFS project the build is delivering assets for.

.Outputs
The name of the TFS project the build is delivering assets for.

.Example
$teamProject = Get-BuildTeamProject
#>
function Get-BuildTeamProject {
    [CmdletBinding()]
    param()
    return $powerdelivery.teamProject
}
tools\PowerDelivery\Get-BuildUri.ps1
<#
.Synopsis
Gets the URI the build when running on TFS.

.Description
Gets the URI the build when running on TFS. This URI is required to 
make some calls to the TFS API and should not be necessary for you 
to use for most operations.

.Outputs
The URI of the build when running on TFS.

.Example
$uri = Get-BuildUri
#>
function Get-BuildUri {
    [CmdletBinding()]
    param()
    return $powerdelivery.buildUri
}
tools\PowerDelivery\Get-BuildWorkspaceName.ps1
<#
.Synopsis
Gets the name of the TFS source control workspace name used to get a copy of the source code to compile.

.Description
Gets the name of the TFS source control workspace name used to get a copy of the source code to compile.

.Outputs
The name of the TFS source control workspace name used to get a copy of the source code to compile.

.Example
$workspaceName = Get-BuildWorkspaceName
#>
function Get-BuildWorkspaceName {
    [CmdletBinding()]
    param()
    return $powerdelivery.workspaceName
}
tools\PowerDelivery\Get-ComputerLocalDeployPath.ps1
<#
.Synopsis
Retrieves the local path on a remote computer to deploy files into.

.Description
Retrieves the local path on a remote computer to deploy files into.

.Parameter computerName
The computer to retrieve the deploy path for.

.Parameter driveLetter
Optional. The drive letter upon which to deploy files. Defaults to "C"

.Example
$deployPath = Get-ComputerLocalDeployPath
#>
function Get-ComputerLocalDeployPath {
    param(
        [Parameter(Position=0,Mandatory=1)][string] $computerName,
        [Parameter(Position=1,Mandatory=0)][string] $driveLetter = $powerdelivery.deployDriveLetter
    )

    $remoteDeployPath = Get-ComputerRemoteDeployPath $computerName -DriveLetter $powerdelivery.deployDriveLetter
    $remoteDeployPath -replace "\\\\$computerName", "$($driveLetter):"
}
tools\PowerDelivery\Get-ComputerRemoteDeployPath.ps1
<#
.Synopsis
Retrieves the UNC path on a remote computer to deploy files into.

.Description
Retrieves the UNC path on a remote computer to deploy files into. This path 
will be a subdirectory named after the build.

.Parameter computerName
The computer to retrieve the deploy path for.

.Parameter driveLetter
Optional. The drive letter upon which to deploy files. Defaults to "C"

.Example
$deployPath = Get-ComputerRemoteDeployPath
#>
function Get-ComputerRemoteDeployPath {
    param(
        [Parameter(Position=0,Mandatory=1)][string] $computerName,
        [Parameter(Position=1,Mandatory=0)][string] $driveLetter = $powerdelivery.deployDriveLetter
    )

    $buildEnvironment = Get-BuildEnvironment

    if (!$powerdelivery.deployShares.ContainsKey($computerName)) {

        if (!$computerName.StartsWith("localhost"))
        {
            New-RemoteShare -computerName $computerName -shareName "PowerDelivery" -shareDirectory "$($driveLetter):\PowerDelivery" | Out-Host
        }

        $buildName = Get-BuildName
        $buildNumber = Get-BuildNumber

        $deployPath = "\\$computerName\PowerDelivery"

        if ($computerName.StartsWith("localhost"))
        {
            $deployPath = "$($driveLetter):\PowerDelivery"

            if (!(Test-Path $deployPath))
            {
                mkdir -Force $deployPath | Out-Null
            }
        }

        $buildPath = "$deployPath\$buildName"
        $buildPathAlias = "$deployPath\$($powerdelivery.scriptName) - $($buildEnvironment)"

        mkdir $buildPath -Force | Out-Null

        $buildMatches = "$($powerdelivery.scriptName) - $($buildEnvironment)*"

        $prevBuildCount = (gci -Directory $deployPath | where-object -Property Name -Like $buildMatches).count

        if ($prevBuildCount -gt 5) {

            Write-Host "Removing builds older than newest 5 on $computerName for $buildMatches"

            $numberToDelete = $prevBuildCount - 5
            
            gci -Directory $deployPath | where-object -Property Name -Like $buildMatches | Sort-Object -Property LastWriteTime | select -first $numberToDelete | Remove-Item -Force -Recurse | Out-Null
        }

        $localDeployPath = $deployPath

        if (!$computerName.StartsWith("localhost"))
        {
            $localDeployPath = $deployPath -replace "\\\\$computerName", "$($driveLetter):"
        }

        $localAliasPath = [System.IO.Path]::Combine($localDeployPath, "$($powerdelivery.scriptName) - $($buildEnvironment)")
        $localBuildPath = [System.IO.Path]::Combine($localDeployPath, $buildName)

        $invokeArgs = @{
            "ArgumentList" = @($localAliasPath, $localBuildPath);
            "ScriptBlock" = {
                param($aliasPath, $buildPath)
                if ((Test-Path -Path $aliasPath)) {
                    & cmd /c "rmdir ""$aliasPath"""
                }

                & cmd /c "mklink /J ""$aliasPath"" ""$buildPath""" | Out-Null
            }
        }

        if (!$computerName.StartsWith("localhost")) {
            $invokeArgs.Add("ComputerName", $computerName)
        }

        Invoke-Command @invokeArgs

        $powerdelivery.deployShares.Add($computerName, $buildPathAlias)
    }
    $powerdelivery.deployShares[$computerName]
}
tools\PowerDelivery\Import-DeliveryModule.ps1
<#
.Synopsis
Imports a delivery module for use by a delivery build script.

.Description
Imports a delivery module for use by a delivery build script. This causes 
powerdelivery to try and find functions that match the following syntax 
in any loaded PowerShell modules:

Invoke-<ModuleName>DeliveryModule<Stage>

Where "ModuleName" is the value passed to the name parameter of this 
function, and "Stage" is any of the delivery block names ("Init", "Commit", 
"SetupEnvironment", "Deploy" etc.) prefixed with "Pre" to run before the 
code in the delivery build script doing the import, or "Post" to run after.

.Parameter name
The name of the delivery module to import functions for.

.Example
Import-DeliveryModule MSBuild
#>
function Import-DeliveryModule {
	[CmdletBinding()]
	param([Parameter(Position=0,Mandatory=1)][string] $name)
	
	$functionName = "Initialize-$($name)DeliveryModule"
	
	if (Get-Command $functionName -ErrorAction SilentlyContinue) {
		& $functionName
		$powerdelivery.deliveryModules += $name
	}
	else {
		throw "Delivery Module $name could not be loaded. Unable to find function $functionName in loaded PowerShell modules."
	}
}
tools\PowerDelivery\Import-Snapin.ps1
function Import-Snapin {
    param(
        [Parameter(Position=0,Mandatory=1)] $ModuleName
    )

    $ModuleLoaded = $false
    $LoadAsSnapin = $false

    if ($PSVersionTable.PSVersion.Major -ge 2) {
        if ((Get-Module -ListAvailable | ForEach-Object {$_.Name}) -contains $ModuleName) {

            Import-Module $ModuleName

            if ((Get-Module | ForEach-Object {$_.Name}) -contains $ModuleName) { 
                $ModuleLoaded = $true 
            } 
            else { 
                $LoadAsSnapin = $true 
            }
        }
        elseif ((Get-Module | ForEach-Object {$_.Name}) -contains $ModuleName) { 
            $ModuleLoaded = $true 
        } 
        else { 
            $LoadAsSnapin = $true 
        }
    }
    else { 
        $LoadAsSnapin = $true 
    }

    if ($LoadAsSnapin) {
        try {
            if ((Get-PSSnapin -Registered | ForEach-Object {$_.Name}) -contains $ModuleName) {
                if ((Get-PSSnapin -Name $ModuleName -ErrorAction SilentlyContinue) -eq $null) { 
                    Add-PSSnapin $ModuleName 
                }

                if ((Get-PSSnapin | ForEach-Object {$_.Name}) -contains $ModuleName) { 
                    $ModuleLoaded = $true 
                }
            }
            elseif ((Get-PSSnapin | ForEach-Object {$_.Name}) -contains $ModuleName) { 
                $ModuleLoaded = $true 
            }
        }
        catch {
            throw "Unable to load $ModuleName snapin or module."
        }
    }
}
tools\PowerDelivery\Install-NServiceBusService.ps1
<#
.Synopsis
Installs a Windows Service for an NServiceBus version 4+ Enterprise Service Bus host.

.Description
Installs a Windows Service for an NServiceBus version 4+ Enterprise Service Bus host. 
You must have already copied the files necessary to run the host, including a copy 
of NServiceBus.Host.exe to a directory local to that computer prior to calling this 
cmdlet. You should call Uninstall-NServiceBusService prior to this cmdlet, 
otherwise files will be in use.

.Parameter ComputerName
The name of the remote computer to install the service on.

.Parameter Name
The name of the service. This can be used to start/stop the service with the "net" command.

.Parameter DisplayName
The name that appears in the services control panel applet.

.Parameter Description
The description that appears in the services control panel applet.

.Parameter Directory
A relative path in the build drop location of files containing the service to install.

.Parameter AccountName
The user account under which the service will be configured to run. This account must already 
exist on the computer specified by the ComputerName parameter.

.Parameter AccountPassword
The password under which the service will be configured to run.

.Parameter DriveLetter
Optional. The drive letter on the target computer to deploy to.

.Example
Install-NServiceBusService `
	-ComputerName MyComputer `
	-Name MyService `
	-DisplayName "My Service" `
	-Description "Does something wonderful" `
	-Directory "C:\Share\MyService" `
	-AccountName "MYDOMAIN\myuser" `
	-AccountPassword "somep@ssword12"
#>

function Install-NServiceBusService {
	param(
		[Parameter(Position=0,Mandatory=1)] $ComputerName,
		[Parameter(Position=1,Mandatory=1)][string] $Name,
		[Parameter(Position=2,Mandatory=1)][string] $DisplayName,
		[Parameter(Position=3,Mandatory=1)][string] $Description,
		[Parameter(Position=4,Mandatory=1)][string] $Directory,
		[Parameter(Position=5,Mandatory=1)][string] $AccountName,
		[Parameter(Position=6,Mandatory=1)][string] $AccountPassword,
        [Parameter(Position=7,Mandatory=0)] $IsMaster = $false,
        [Parameter(Position=8,Mandatory=0)] $IsDistributor = $false,
        [Parameter(Position=9,Mandatory=0)][string] $DistributorAddress,
        [Parameter(Position=10,Mandatory=0)][string] $EndpointConfigurationType,
        [Parameter(Position=11,Mandatory=0)][string] $DriveLetter = $powerdelivery.deployDriveLetter
	)

	Set-Location $powerdelivery.deployDir

    $logPrefix = "Install-NServiceBusService:"

    if ($IsMaster -and $IsDistributor) {
        throw "An instance cannot be a distributor and a master."
    }

    if (($IsMaster -or $IsDistributor) -and ![String]::IsNullOrWhiteSpace($DistributorAddress)) {
        throw "The distributor address does not need to be specified for a master or distributor."
    }

    $computerNames = $ComputerName -split "," | % { $_.Trim() }

    foreach ($curComputerName in $computerNames) {

        Add-CredSSPTrustedHost $curComputerName

        $dropServicePath = Join-Path $powerdelivery.deployDir $Directory
        
        $remoteDeployPath = Get-ComputerRemoteDeployPath $curComputerName -DriveLetter $DriveLetter
        $remoteServicePath = Join-Path $remoteDeployPath $Directory

        $localDeployPath = Get-ComputerLocalDeployPath $curComputerName -DriveLetter $DriveLetter
        $localServicePath = Join-Path $localDeployPath $Directory

        "$logPrefix Creating $remoteServicePath"
        mkdir -Force $remoteServicePath | Out-Null

        "$logPrefix Copying $dropServicePath\* to $remoteServicePath"
        copy "$dropServicePath\*" $remoteServicePath -Force -Recurse | Out-Null

        "$logPrefix Installing $Name service on $curComputerName as $($AccountName)..."

        $secpasswd = ConvertTo-SecureString $AccountPassword -AsPlainText -Force
        $accountCreds = New-Object System.Management.Automation.PSCredential ($AccountName, $secpasswd)

	    Invoke-Command -ComputerName $curComputerName -Authentication Credssp -Credential $accountCreds {

		    $installServiceArgs = @(
		 	    "-install",
		 	    "-serviceName",
		 	    $using:Name,
		 	    "-displayName",
		 	    "`"$using:DisplayName`"",
		 	    "-description",
		 	    "`"$using:Description`"",
		 	    "-username",
		 	    $using:AccountName,
		 	    "-password",
		 	    $using:AccountPassword
		    )

            if (![String]::IsNullOrWhiteSpace($using:EndpointConfigurationType)) {
                $installServiceArgs.Add('EndpointConfigurationType', $using:EndpointConfigurationType)
            }

            cd $using:localServicePath
            Write-Host "$using:logPrefix $using:localServicePath\NServiceBus.Host.exe $installServiceArgs"
	 	    & "$using:localServicePath\NServiceBus.Host.exe" @installServiceArgs

	 	    if ($LASTEXITCODE -ne 0) {
	 		    throw "Error installing $using:Name - return code was $LASTEXITCODE"
		    }
	 	    else {
	 		    Write-Host "$using:Name service installed on $using:curComputerName."
		    }

	 	    "$using:logPrefix Starting $using:Name service on $using:curComputerName ..."

            Get-Service -ComputerName $using:curComputerName $using:Name | Restart-Service
	
	        Do {
		        Start-Sleep -Seconds 5
                "$using:logPrefix Polling for $using:Name on $using:curComputerName to finish restarting..."
	        } Until ((Get-Service -ComputerName $using:curComputerName $using:Name).Status -eq "Running")

            "$using:logPrefix $using:Name service started on $using:curComputerName successfully."
        }
	}
}
tools\PowerDelivery\Invoke-BuildConfigSection.ps1
<#
.Synopsis
Invokes a PowerShell cmdlet passing a section of YAML from the build environment configuration as arguments to it.

.Description
Invokes a PowerShell cmdlet passing a section of YAML from the build environment configuration as arguments to it.

.Parameter section
hash - Each value of the hash will be passed to the cmdlet as arguments.

.Parameter cmdlet
string - The name of the cmdlet to invoke.

.Example
In the example below, there is a YAML configuration section named "Database" with 
settings that match the arguments of the "Invoke-Roundhouse" cmdlet.

$databaseSection = Get-BuildSetting Database
Invoke-BuildConfigSection $databaseSection Invoke-Roundhouse 
#>
function Invoke-BuildConfigSection {
	[CmdletBinding()]
	param(
		[Parameter(Position=0,Mandatory=1)] $section,
		[Parameter(Position=1,Mandatory=1)][string] $cmdlet
	)
	
	$invokeArgs = @{}

	if ($section.Keys) {
		$section.Keys | % {
			$sectionValue = $section[$_]
			if ($sectionValue.GetType().Name -ne 'Hashtable' -and $sectionValue.StartsWith(":")) {
				$present = $sectionValue.Substring(1)
				$isPresent = [boolean]$present
				$switchParameter = New-Object System.Management.Automation.SwitchParameter -ArgumentList @($isPresent)
				$invokeArgs.Add($_, $switchParameter)
			}
			else {
				$invokeArgs.Add($_, $section[$_])	
			}
		}
	}

	Invoke-Expression "& $cmdlet @invokeArgs"
}
tools\PowerDelivery\Invoke-BuildConfigSections.ps1
<#
.Synopsis
Calls the Invoke-BuildConfigSection cmdlet once for each entry in a hash. 

.Description
Calls the Invoke-BuildConfigSection cmdlet once for each entry in a hash. Use this to do 
work on all nested entries in a section of YAML from the build environment configuration.

.Parameter sections
hash - Each value of the hash will be passed to Invoke-BuildConfigSection cmdlet.

.Parameter cmdlet
string - The name of the cmdlet to invoke.

.Example
In th example below, there is a YAML configuration section named "MSBuild" with YAML sections 
below it. Each entry below it has settings that match the arguments of the "Invoke-MSBuild" cmdlet.

$msBuildSection = Get-BuildSetting MSBuild
Invoke-BuildConfigSections $msBuildSection Invoke-MSBuild
#>
function Invoke-BuildConfigSections {
	[CmdletBinding()]
	param(
		[Parameter(Position=0,Mandatory=1)] $sections,
		[Parameter(Position=1,Mandatory=1)][string] $cmdlet
	)
	
	$sections.Keys | % {
		$section = $sections[$_]
		Invoke-BuildConfigSection $section $cmdlet
	}
}
tools\PowerDelivery\Invoke-MSBuild.ps1
<#
.Synopsis
Compiles a project using msbuild.exe.

.Description
The Invoke-MSBuild cmdlet is used to compile a MSBuild-compatible project or solution. You should always use this cmdlet instead of a direct call to msbuild.exe or existing cmdlets you may have found online when working with powerdelivery.

This cmdlet provides the following essential continuous delivery features:

Updates the version of any AssemblyInfo.cs (or AssemblyInfo.vb) files with the current build version. This causes all of your binaries to have the build number. For example, if your build pipeline's version in the script is set to 1.0.2 and this is a build against changeset C234, the version of your assemblies will be set to 1.0.2.234.

Automatically targets a build configuration matching the environment name ("Commit", "Test", or "Production"). Create build configurations named "Commit", "Test", and "Production" with appropriate settings in your projects for this to work. If you don't want this, you'll have to explicitly pass the configuration as a parameter.

Reports the status of the compilation back to TFS to be viewed in the build summary. This is important because it allows tests run using mstest.exe to have their run results associated with the compiled assets created using this cmdlet.

.Example
Invoke-MSBuild MyProject/MySolution.sln -properties  @{MyCustomProp = SomeValue}

.Parameter projectFile
A relative path at or below the script directory that specifies an MSBuild project or solution to compile.

.Parameter properties
Optional. A PowerShell hash containing name/value pairs to set as MSBuild properties.

.Parameter target
Optional. The name of the MSBuild target to invoke in the project file. Defaults to the default target specified within the project file.

.Parameter toolsVersion
Optional. The version of MSBuild to run ("2.0", "3.5", "4.0", etc.). The default is "4.0".

.Parameter verbosity
Optional. The verbosity of this MSBuild compilation. The default is "m".

.Parameter buildConfiguration
Optional. The default is to use the same as the environment name. Create build configurations named "Commit", "Test", and "Production" with appropriate settings in your projects.

.Parameter flavor
Optional. The platform configuration (x86, x64 etc.) of this MSBuild complation. The default is "AnyCPU".

.Parameter ignoreProjectExtensions
Optional. A semicolon-delimited list of project extensions (".smproj;.csproj" etc.) of projects in the solution to not compile.

.Parameter dotNetVersion
Optional. The .NET version to use for compilation. Defaults to the version specified in the project file(s) being built.
#>
function Invoke-MSBuild {
    [CmdletBinding()]
    param(
        [Parameter(Position=0,Mandatory=1)][string] $projectFile, 
        [Parameter(Position=1,Mandatory=0)] $properties = @{}, 
        [Parameter(Position=2,Mandatory=0)][string] $target, 
        [Parameter(Position=3,Mandatory=0)][string] $toolsVersion, 
        [Parameter(Position=4,Mandatory=0)][string] $verbosity = "m", 
        [Parameter(Position=5,Mandatory=0)][string] $buildConfiguration, 
        [Parameter(Position=6,Mandatory=0)][string] $flavor = "AnyCPU", 
        [Parameter(Position=7,Mandatory=0)][string] $ignoreProjectExtensions, 
        [Parameter(Position=8,Mandatory=0)][string] $dotNetVersion = "4.0"
    )
	
    $logPrefix = "Invoke-MSBuild:"

	if ([String]::IsNullOrWhiteSpace($buildConfiguration)) {
		if ((Get-BuildEnvironment) -eq 'Local') {
			$buildConfiguration = 'Debug'
		}
		else {
			$buildConfiguration = 'Release'
		}

		if (!$properties.ContainsKey('Configuration')) {
			$properties.Add('Configuration', $buildConfiguration)
		}
	}
	
	$dropLocation = Get-BuildDropLocation
	$logFolder = Join-Path $dropLocation "Logs"
	mkdir -Force $logFolder | Out-Null

    $regKey = "HKLM:\Software\Microsoft\MSBuild\ToolsVersions\$dotNetVersion"
    $regProperty = "MSBuildToolsPath"

    $msbuildExe = Join-Path -path (Get-ItemProperty $regKey).$regProperty -childpath "msbuild.exe"

    $msBuildCommand = "& ""$msbuildExe"""
    $msBuildCommand += " /nologo /m"

    if ($properties.length -gt 0) {
        
        $properties.Keys | % {
            $msBuildCommand += " ""/p:$($_)=$($properties.Item($_))"""
        }
    }
	
    if ([string]::IsNullOrWhiteSpace($toolsVersion) -eq $false) {
        $msBuildCommand += " ""/tv:$toolsVersion"""
    }

    $msBuildCommand += " `"/consoleloggerparameters:Verbosity=q`""

    if ([string]::IsNullOrWhiteSpace($verbosity) -eq $false) {
        $msBuildCommand += " /v:$verbosity"
    }

    if ([string]::IsNullOrWhiteSpace($ignoreProjectExtensions) -eq $false) {
        $msBuildCommand += " ""/ignore:$ignoreProjectExtensions"""
    }

	if (![string]::IsNullOrWhiteSpace($target)) {
		$msBuildCommand += " ""/T:$target"""
	}
	
	$projectFileBase = [IO.Path]::GetFileNameWithoutExtension($projectFile)
	$logFile = "$($projectFileBase).log"
	
	$msBuildCommand += " ""/l:FileLogger,Microsoft.Build.Engine;logfile=$logFile"""
    $msBuildCommand += " ""$projectFile"""
    
	$currentDirectory = Get-Location

    if (Get-BuildOnServer) {
			
		$fullProjectFile = Join-Path $currentDirectory $projectFile
		$shortPath = [System.IO.Path]::GetDirectoryName($fullProjectFile)
		
	    Update-AssemblyInfoFiles -path $shortPath
	}

    try {
        Write-Host
        "$logPrefix $msBuildCommand"
        Exec -errorMessage "Invocation of MSBuild project $projectFile failed." {            
            Invoke-Expression $msBuildCommand | Out-Host
        }
    }
    finally {
        if (Get-BuildOnServer) {
     
            $buildDetail = Get-CurrentBuildDetail

            $projectFileName = [System.IO.Path]::GetFileName($projectFile)
            $tfsPath = "`$/$($projectFile.Replace('\', '/'))"

            $publishTarget = "Default"
            if (![string]::IsNullOrWhiteSpace($target)) {
		        $publishTarget = $target
	        }
			
			$logFilename = [IO.Path]::GetFileName($logFile)
			$logDestFile = Join-Path $logFolder $logFilename
			
			copy $logFile $logDestFile

            $buildProjectNode = [Microsoft.TeamFoundation.Build.Client.InformationNodeConverters]::AddBuildProjectNode(`
                $buildDetail.Information, [DateTime]::Now, $buildConfiguration, $projectFile, $flavor, $tfsPath, [DateTime]::Now, $publishTarget)
				
			$errorCount = 0
			$warningCount = 0
						
			Get-Content $logFile | Where-Object {$_ -like "*error*"} | % { 
				if ($_ -match "^.*(?=: error)") {
					$errorCount++
					
					$parensStart = $Matches[0].IndexOf('(')
					$parensEnd = $Matches[0].IndexOf(')')
					$lineSep = $Matches[0].IndexOf(',')
					
					$errorStart = $_.IndexOf(": error")
					
					$fileName = ""
					$lineNumber = 0
					$lineCharacter = 0
					
					if ($parensStart -eq -1 -or $parensEnd -eq -1) {
						$fileName = $Matches[0].Substring(0, $errorStart)
					}
					else {
						$fileName = $Matches[0].Substring(0, $parensStart)
						$lineNumber = $Matches[0].Substring($parensStart + 1, $lineSep - ($parensStart + 1))
						$lineCharacter = $Matches[0].Substring($lineSep + 1, $parensEnd - ($lineSep + 1))
					}
					
					$buildError = [Microsoft.TeamFoundation.Build.Client.InformationNodeConverters]::AddBuildError(`
						$buildProjectNode.Node.Children, "Compilation", $fileName, $lineNumber, $lineCharacter, "", $_.Substring($errorStart + 2), [DateTime]::Now)
				}
			}
			
			Get-Content $logFile | Where-Object {$_ -like "*warning*"} | % { 
				if ($_ -match "^.*(?=: warning)") {
					$warningCount++
					
					$parensStart = $Matches[0].IndexOf('(')
					$parensEnd = $Matches[0].IndexOf(')')
					$lineSep = $Matches[0].IndexOf(',')
					
					$warningStart = $_.IndexOf(": warning")
					
					$fileName = ""
					$lineNumber = 0
					$lineCharacter = 0
					
					if ($parensStart -eq -1 -or $parensEnd -eq -1) {
						$fileName = $Matches[0].Substring(0, $warningStart)
					}
					else {
						$fileName = $Matches[0].Substring(0, $parensStart)
						$lineNumber = $Matches[0].Substring($parensStart + 1, $lineSep - ($parensStart + 1))
						$lineCharacter = $Matches[0].Substring($lineSep + 1, $parensEnd - ($lineSep + 1))
					}
					
					$buildWarning = [Microsoft.TeamFoundation.Build.Client.InformationNodeConverters]::AddBuildWarning(`
						$buildProjectNode.Node.Children, $fileName, $lineNumber, $lineCharacter, "", $_.Substring($warningStart + 2), [DateTime]::Now, "Compilation")
				}
			}
			
			$buildProjectNode.CompilationErrors = $errorCount
			$buildProjectNode.CompilationWarnings = $warningCount

			$logDestUri = New-Object -TypeName System.Uri -ArgumentList $logDestFile

			$logFileLink = [Microsoft.TeamFoundation.Build.Client.InformationNodeConverters]::AddExternalLink(`
				$buildProjectNode.Node.Children, "Log File", $logDestUri)

            $buildProjectNode.Save()

            $buildDetail.Information.Save()

			Write-BuildSummaryMessage -name "Compile" -header "Compilations" -message "MSBuild: $projectFile ($flavor - $buildConfiguration)"
        }
    }
}
tools\PowerDelivery\Invoke-MSTest.ps1
<#
.Synopsis
Runs unit tests using mstest.exe.

.Description
The Invoke-MSTest cmdlet is used to run unit or acceptance tests using mstest.exe. You should always use this cmdlet instead of a direct call to mstest.exe or existing cmdlets you may have found online when working with powerdelivery.

This cmdlet reports the results of the test run back to TFS to be viewed in the build summary.

IMPORTANT: You most only call Invoke-MSTest in the TestUnits or TestAcceptance blocks.

.Example
Invoke-MSTest -file MyTests.dll -results MyTestResults.trx -category AllTests

.Parameter file
string - The path to a file containing MSTest unit tests

.Parameter results
string - A path relative to the drop location (retrieved via Get-BuildDropLocation) of a test run results file (.trx) to store results in.

.Parameter category
string - Runs tests found in the file referenced by the file parameter on any classes found with the [TestCategory] attribute present set to this value.

.Parameter computerName
string - Optional. A remote computer on which to run the tests.

.Parameter platform
string - Optional. The platform configuration (x86, x64 etc.) of the project compiled using Invoke-MSBuild containing the tests that were run. The default is "AnyCPU".

.Parameter buildConfiguration
string - Optional. The default is to use the Release configuration.
#>
function Invoke-MSTest {
    [CmdletBinding()]
    param(
		[Parameter(Position=0,Mandatory=1)][string] $file,
		[Parameter(Position=1,Mandatory=1)][string] $results,
		[Parameter(Position=2,Mandatory=1)][string] $category,
        [Parameter(Position=5,Mandatory=0)][string] $testSettings,
        [Parameter(Position=5,Mandatory=0)][string] $platform = 'AnyCPU',
		[Parameter(Position=6,Mandatory=0)][string] $buildConfiguration
    )
	
	Set-Location $powerdelivery.deployDir

    $logPrefix = "Invoke-MSTest:"
	
	$environment = Get-BuildEnvironment
	$dropLocation = Get-BuildDropLocation
	$currentDirectory = Get-Location

    $localResults = Join-Path $currentDirectory $results
	
	$fileName = [System.IO.Path]::GetFileName($file)
	$testsDir = [System.IO.Path]::GetDirectoryName($file)

	if ([String]::IsNullOrWhiteSpace($buildConfiguration)) {
		if ($environment -eq 'Local') {
			$buildConfiguration = 'Debug'
		}
		else {
			$buildConfiguration = 'Release'
		}
	}

    $localTestsDir = Join-Path $currentDirectory $testsDir
    $dropTestsDir = "$($dropLocation)$testsDir"

	$dropResults = "$dropLocation\$results"

    try {
        $localTestSettings = $null
        if (![String]::IsNullOrWhiteSpace($testSettings)) {
            $dropTestSettings = Join-Path $dropLocation $testSettings
            if (!(Test-Path $dropTestSettings -PathType Leaf)) {
                throw "Couldn't find test settings file $testSettings"
            }

            $localTestSettings = Join-Path $currentDirectory $testSettings
            copy -Force $dropTestSettings $localTestSettings | Out-Null
        }
            
		$filePath = $file

		$localResults = Join-Path $currentDirectory $results

		rm -ErrorAction SilentlyContinue -Force $localResults | Out-Null
	
		Exec -errorMessage "Error running tests in $filePath" {
            $command = "mstest /testcontainer:`"$filePath`" /category:`"$category`" /resultsfile:`"$localResults`" /usestderr /nologo"
            if ($localTestSettings -ne $null) {
                $command += " /testsettings:`"$($testSettings)`""
            }

            Write-Host "$logPrefix $command"
            Invoke-Expression $command
            Write-Host
		}
	}
	finally {
		
        if ($powerdelivery.onServer) {
	
		    if (Test-Path $localResults -PathType Leaf) {

                copy $localResults $dropResults | Out-Null

                # Publish acceptance test results for this build to the TFS server
                Exec -errorMessage "Error publishing test results for $dropResults" {

                    $command = "mstest /publish:`"$(Get-BuildCollectionUri)`" /teamproject:`"$(Get-BuildTeamProject)`" /publishbuild:`"$(Get-BuildName)`" /publishresultsfile:`"$dropResults`" /flavor:$buildConfiguration /platform:$platform /nologo"
                    Write-Host "$logPrefix $command"
                    Invoke-Expression "$command"
                }
			
			    Write-BuildSummaryMessage -name "TestUnits" -header "Unit Tests" -message "MSTest: $file -> $results"
            }
        }
	}
}
tools\PowerDelivery\Invoke-Powerdelivery.ps1
<#
.Synopsis
Runs a continuous delivery build script using powerdelivery.

.Description
Runs a continuous delivery build script using powerdelivery. You should only ever
specify the first parameter of this function when running this function on your own 
computer. All other parameters are used by the TFS server.

.Example
Invoke-PowerDelivery .\MyProduct.ps1

.Parameter buildScript
The relative path to to a local powerdelivery build script to run.
#>
function Invoke-Powerdelivery {
    [CmdletBinding()]
    param(
        [Parameter(Position=0,Mandatory=1)][string] $buildScript,
        [Parameter(Position=1,Mandatory=0)][switch] $onServer = $false,
	    [Parameter(Position=2,Mandatory=0)][string] $dropLocation,
	    [Parameter(Position=3,Mandatory=0)][string] $changeSet,
	    [Parameter(Position=4,Mandatory=0)][string] $requestedBy,
	    [Parameter(Position=5,Mandatory=0)][string] $teamProject,
	    [Parameter(Position=6,Mandatory=0)][string] $workspaceName,
	    [Parameter(Position=7,Mandatory=0)][string] $environment = 'Local',
        [Parameter(Position=8,Mandatory=0)][string] $buildUri,
        [Parameter(Position=9,Mandatory=0)][string] $collectionUri,
	    [Parameter(Position=10,Mandatory=0)][string] $priorBuild
    )
	
	$logPrefix = "Powerdelivery:"
	
	$ErrorActionPreference = 'Stop'

	function InvokePowerDeliveryModuleHook($blockName, $stage) {
		$actionPerformed = $false

		$powerdelivery.blockName = $blockName

		$powerdelivery.moduleHooks["$stage$blockName"] | % { 
			& $_ 
			$actionPerformed = $true
		}
		$powerdelivery.hookResult = $actionPerformed
	}

	function InvokePowerDeliveryBuildAction($condition, $stage, $description, $status, $blockName) {
		if ($condition) {
			$actionPerformed = $false

			$powerdelivery.blockName = $blockName
			
            if ($blockName -ne "Init") {
			    Write-Host
			    Write-ConsoleSpacer
			    "= $logPrefix $status..."
	    	    Write-ConsoleSpacer
	            Write-Host
            }
			
			try {
				InvokePowerDeliveryModuleHook $blockName 'Pre'
				if ($powerdelivery.hookResult) {
					$actionPerformed = $true
				}
			    if ($stage) {
		        	& $stage
					$actionPerformed = $true
				}			
				if ($blockName -eq "Compile") {
					$yamlConfig = Get-BuildConfig
					$assetOperations =  $yamlConfig.Assets

					if ($assetOperations) {
						$assetOperations.Keys | % {
							$invokeArgs = @{}

							$assetOperation = $assetOperations[$_]
							
							if ($assetOperation.Path) {
								$invokeArgs.Add('path', $assetOperation.Path)
							}
							if ($assetOperation.Destination) {
								$invokeArgs.Add('destination', $assetOperation.Destination)
							}
							if ($assetOperation.Filter) {
								$invokeArgs.Add('filter', $assetOperation.Filter)
							}
							if ($assetOperation.Recurse) {
								$invokeArgs.Add('Recurse', $true)
							}
							& Publish-BuildAssets @invokeArgs	
						}
					}
				}
				InvokePowerDeliveryModuleHook $blockName 'Post'
				if ($powerdelivery.hookResult) {
					$actionPerformed = $true
				}
				
				$message = "No actions performed."
				
				if ($actionPerformed) {
					$message = "Successful."
				}
			}
			finally {
			   	Set-Location $powerdelivery.currentLocation
			}
		}
	}
	
	function MergeHashNested($baseHash, $subHash) {
		$mergedHash = @{}
		$baseHash.Keys | % {
			if (!$subHash.ContainsKey($_)) {
				$mergedHash.Add($_, $baseHash[$_])
			}
			else {		
				$baseHashVal = $baseHash[$_]
				if ($baseHashVal.GetType().Name -eq 'Hashtable') {
					$childMergedHash = MergeHashNested -baseHash $baseHashVal -subHash $subHash[$_]
					$mergedHash.Add($_, $childMergedHash)
				}
				else {
					$mergedHash.Add($_, $subHash[$_])
				}
			}
		}
		$subHash.Keys | % {
            if ($baseHash -eq $null -or !$baseHash.ContainsKey($_)) {
				$mergedHash.Add($_, $subHash[$_])
			}
		}
		$mergedHash
	}
	
	function ReplaceReferencedConfigSettings($yamlNodes, $replaceFor = @()) {
		
        if ($yamlNodes.Keys) {
			$replacedValues = @{}
			$yamlNodes.Keys | % {
				$yamlNode = $yamlNodes[$_]			
				if ($yamlNode.GetType().Name -eq 'Hashtable') {

                    $subReplacements = $replaceFor | % { $_ }
                    $subReplacements += $_

					ReplaceReferencedConfigSettings -yamlNodes $yamlNode -replaceFor $subReplacements
				}
				else {
					$matches = Select-String "\<<.*?\>>" -InputObject $yamlNode -AllMatches | Foreach {$_.Matches}

					$replacedValue = $yamlNode
					$matches | Foreach { 

						$envSettingName = $_.Value.Substring(2, $_.Length - 4)
						$envSettingValue = [String]::Empty
    
	                    foreach ($replacement in $replaceFor) {
	                        if ($envSettingName.Equals($replacement, [System.StringComparison]::InvariantCultureIgnoreCase)) {
	                            throw "Configuration setting $envSettingName and $replacement refer to each other causing a circular dependency"
	                        }
	                    }

	                    if ($envSettingName -like "Credentials:*") {
	                        $userName = $envSettingName.Substring(12)
	                        $replacedValue = Get-BuildCredentials $userName
	                    }
	                    else {
							try {
								$envSettingValue = Get-BuildSetting $envSettingName
							}
							catch {
							    if ($envSettingName -eq "BuildAppVersion") {
    					    	    $envSettingValue = $powerdelivery.buildAppVersion
    					    	}
						    	elseif ($envSettingName -eq "BuildEnvironment") {
						    	    $envSettingValue = $powerdelivery.environment
    					    	}
						    	elseif ($envSettingName -eq "BuildNumber") {
							    	$envSettingValue = $powerdelivery.buildNumber
						    	}
						    	elseif ($envSettingName -eq "BuildDropLocation") {
							    	$envSettingValue = $powerdelivery.dropLocation
						    	}
						    	else {
						    		$errorMessage = $_.Exception.Message
								    throw "Error replacing setting in module configuration file: $errorMessage"
						    	}
							}

	                        if ($envSettingValue.GetType().Name -eq 'Hashtable') {
	                                
	                            $subReplacements = $replaceFor | % { $_ }
	                            $subReplacements += $envSettingName
	                                
	                            $replacedValue = ReplaceReferencedConfigSettings -yamlNodes $envSettingValue -replaceFor $subReplacements
	                        }
	                        else {
	                            $subReplacements = $replaceFor | % { $_ }
	                            $subReplacements += $envSettingName

	                            $forwardNodes = New-Object System.Collections.Hashtable
	                            $forwardNodes.Add($envSettingName, $envSettingValue)

	                            ReplaceReferencedConfigSettings -yamlNodes $forwardNodes -replaceFor $subReplacements

							    $replacedValue = $replacedValue -replace $_, $forwardNodes[$envSettingName]
	                        }
	                    }
					}
					$replacedValues.Add($_, $replacedValue)
				}
			}
			$replacedValues.GetEnumerator() | Foreach {
				$yamlNodes[$_.Name] = $_.Value
			}
		}
	}
	
	function PrintSpaces($numSpaces, $forYaml) {
		$val = ""
		$spaceIndex = 0
		while ($spaceIndex -lt $numSpaces) {
            if (!$forYaml -and $spaceIndex -eq 0) {
                $val += ".."
            }
            else {
			    $val += ".."
            }
			$spaceIndex++
		}
		$val
	}

	function PrintConfiguration($configNodes, $depth, $forYaml) {
	
		$envMessage = ""
		
		foreach ($configSetting in $configNodes.GetEnumerator() | Sort Name) {
			$envValue = $configSetting.Value
			
			if ($envValue.GetType().Name -eq 'Hashtable') {
				$newDepth = $depth + 1
				$nestedValSpaces = PrintSpaces -numSpaces $depth -forYaml $forYaml
				$nestedVal = "$nestedValSpaces$($configSetting.Key):`r`n"
				$nestedVal += (PrintConfiguration -configNodes $envValue -depth $newDepth -forYaml $forYaml)
				$envMessage += $nestedVal
			}
			else {		
				if ($configSetting.Key.EndsWith("Password")) {
	                $envValue = '********'
	            }
				$envValWithSpaces = PrintSpaces -numSpaces $depth -forYaml $forYaml
				$envMessage += "{0}{1}: {2}`r`n" -f $envValWithSpaces, $configSetting.Key, $envValue
			}
		}
		
		$envMessage
	}

    if (!$dropLocation.EndsWith('\')) {
	    $dropLocation = "$($dropLocation)\"
    }
	
	$powerdelivery.moduleHooks = @{
		"PreInit" = @(); "PostInit" = @();
		"PreCompile" = @(); "PostCompile" = @();
		"PreTestEnvironment" = @(); "PostTestEnvironment" = @();
		"PreDeploy" = @(); "PostDeploy" = @();
		"PreTestAcceptance" = @(); "PostTestAcceptance" = @();
		"PreTestUnits" = @(); "PostTestUnits" = @();
		"PreTestCapacity" = @(); "PostTestCapacity" = @()
	}

    $powerdelivery.deployDriveLetter = "C"
    $powerdelivery.deployShares = @{}
    $powerdelivery.buildCredentials = @{}
	$powerdelivery.deliveryModules = @()
    $powerdelivery.assemblyInfoFiles = @()
    $powerdelivery.currentLocation = gl
    $powerdelivery.noReleases = $true
    $powerdelivery.config = @()
    $powerdelivery.environment = $environment
    $powerdelivery.dropLocation = $dropLocation
    $powerdelivery.changeSet = $changeSet
    $powerdelivery.requestedBy = $requestedBy
    $powerdelivery.teamProject = $teamProject
    $powerdelivery.workspaceName = $workspaceName
    $powerdelivery.collectionUri = $collectionUri
    $powerdelivery.buildUri = $buildUri
    $powerdelivery.onServer = $onServer
    $powerdelivery.buildNumber = $null
    $powerdelivery.buildName = $null
	$powerdelivery.priorBuild = $priorBuild
	$powerdelivery.deployDir = Join-Path (Get-Location) "PowerDeliveryDeploy"

	if ((Test-Path $powerdelivery.deployDir) -and ($onServer -eq $true -or $environment -eq "Local")) {
		Remove-Item -Path $powerdelivery.deployDir -Force -Recurse | Out-Null
	}
	
	mkdir -Force $($powerdelivery.deployDir) | Out-Null

    Write-Host
	$powerdelivery.version = Get-Module powerdelivery | select version | ForEach-Object { $_.Version.ToString() }
    "powerdelivery $($powerdelivery.version) - https://github.com/eavonius/powerdelivery"
	Write-Host
	$appScript = [System.IO.Path]::GetFileNameWithoutExtension($buildScript)
    $powerdelivery.scriptName = $appScript

    $TranscriptFile = Join-Path $powerdelivery.currentLocation "$($powerdelivery.scriptName)Build.log"
    Start-Transcript -Path $TranscriptFile

    try {
		if ($onServer -eq $true) {

		    Require-NonNullField -variable $changeSet -errorMsg "-changeSet parameter is required when running on TFS"
		    Require-NonNullField -variable $requestedBy -errorMsg "-requestedBy parameter is required when running on TFS"
		    Require-NonNullField -variable $teamProject -errorMsg "-teamProject parameter is required when running on TFS"
		    Require-NonNullField -variable $workspaceName -errorMsg "-workspaceName parameter is required when running on TFS"
		    Require-NonNullField -variable $environment -errorMsg "-environment parameter is required when running on TFS"
            Require-NonNullField -variable $collectionUri -errorMsg "-collectionUri parameter is required when running on TFS"
            Require-NonNullField -variable $buildUri -errorMsg "-buildUri parameter is required when running on TFS"
            Require-NonNullField -variable $dropLocation -errorMsg "-dropLocation parameter is required when running on TFS"
			
			if ($powerdelivery.environment -ne "Local") {
			
				LoadTFS
				
				$powerdelivery.collection = [Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory]::GetTeamProjectCollection($collectionUri)
		        $powerdelivery.buildServer = $powerdelivery.collection.GetService([Microsoft.TeamFoundation.Build.Client.IBuildServer])
		        $powerdelivery.structure = $powerdelivery.collection.GetService([Microsoft.TeamFoundation.Server.ICommonStructureService])
				
				$buildServerVersion = $powerdelivery.buildServer.BuildServerVersion
				
				if ($buildServerVersion -eq 'v3') {
					$powerdelivery.tfsVersion = '2010'
				}
				elseif ($buildServerVersion -eq 'v4') {
					$powerdelivery.tfsVersion = '2012'
				}
				else {
					throw "TFS server must be version 2010 or 2012, a different version was detected."
				}

		        $powerdelivery.projectInfo = $powerdelivery.structure.GetProjectFromName($teamProject)
		        if (!$powerdelivery.projectInfo) {
		            throw "Project '$teamProject' not found in TFS collection '$collectionUri'"
		        }
				
				$powerdelivery.groupSecurity = $powerdelivery.collection.GetService([Microsoft.TeamFoundation.Server.IGroupSecurityService])
		        $powerdelivery.appGroups = $powerdelivery.groupSecurity.ListApplicationGroups($powerdelivery.projectInfo.Uri)
			}
	    }
	    else {
		    $powerdelivery.requestedBy = whoami
			$currentDirectory = Get-Location
			$powerdelivery.dropLocation = [System.IO.Path]::Combine($currentDirectory, "$($appScript)BuildDrop")
			
			if ($powerdelivery.environment -eq 'Local') {
				if (Test-Path $powerdelivery.dropLocation) {
					Remove-Item -Path $powerdelivery.dropLocation -Force -Recurse | Out-Null
				}
			}

			mkdir $powerdelivery.dropLocation -Force | Out-Null
			$dropLocation = $powerdelivery.dropLocation
	    }
		
		$envConfigFileName = "$($appScript)$($environment)"
		$sharedConfigFileName = "$($appScript)Shared.yml"
		$yamlFile = "$($envConfigFileName).yml"

	  if (Test-Path -Path $yamlFile) {
			$yamlPath = (Resolve-Path ".\$($yamlFile)")
			$powerdelivery.config = Get-Yaml -FromFile $yamlPath
	  }
		else {
			Write-Host (Get-Location)
			throw "Build configuration file $yamlFile not found."
	  }

	  if ($onServer) {
        Enable-PSRemoting -Force | Out-Null
        Enable-WSManCredSSP -Role Server -Force | Out-Null
    }
		
		if (Test-Path -Path $sharedConfigFileName) {
			$yamlPath = (Resolve-Path ".\$($sharedConfigFileName)")
			$loadedSharedConfig = $false
			if (Test-Path $yamlPath) {
                try {
				    $sharedConfig = Get-Yaml -FromFile $yamlPath -ErrorAction SilentlyContinue
				    $loadedSharedConfig = $true
                }
                catch {
                    "WARNING: Tried to load $sharedConfigFileName but was empty."
                }
			}
			else {
				Write-Host "WARNING: File $yamlPath not found, not loading shared environment configuration."
			}
			if ($loadedSharedConfig) {
				$powerdelivery.config = MergeHashNested -baseHash $sharedConfig -subHash $powerdelivery.config			
			}
		}
		
		Invoke-Expression -Command ".\$appScript"

		Write-Host
		Write-ConsoleSpacer
	    "= Deployment Pipeline"
		Write-ConsoleSpacer
		"`nName: $appScript"
		"Version: $($powerdelivery.buildAppVersion)`n"
	    
		Write-ConsoleSpacer
	    "= PowerShell Script Parameters"
	    Write-ConsoleSpacer

	    $scriptParams = @{}

	    $scriptParams["Requested By"] = $powerdelivery.requestedBy
	    $scriptParams["Environment"] = $powerdelivery.environment
		
		if ($onServer) {
	        $scriptParams["Team Collection"] = $powerdelivery.collectionUri
	        $scriptParams["Team Project"] = $powerdelivery.teamProject
	        $scriptParams["Change Set"] = $powerdelivery.changeSet
	    
	        $buildNameSegments = $powerdelivery.dropLocation.split('\') | where {$_}
	        $buildNameIndex = $buildNameSegments.length - 1
	        $buildName = $buildNameSegments[$buildNameIndex]
	        $powerdelivery.buildName = $buildName
	        $scriptParams["Build Name"] = $powerdelivery.buildName

	        $buildNumber = $powerdelivery.buildUri.Substring($buildUri.LastIndexOf("/") + 1)
	        $powerdelivery.buildNumber = $buildNumber
	        $scriptParams["Build Number"] = $buildNumber
	    
	        if ($powerdelivery.environment -ne "Local" -and $powerdelivery.environment -ne "Commit") {
	            $scriptParams["Prior Build"] = $powerdelivery.priorBuild
	        }

            Write-BuildSummaryMessage -name "Application" -header "Release" -message "Version: $($powerdelivery.buildAppVersion)`nEnvironment: $($powerdelivery.environment)`nBuild: $($powerdelivery.buildNumber)"
	    }
	    else {

	      $now = Get-Date -Format "yyyyMMdd_hhmmss"

	      $buildNameSegments = $powerdelivery.dropLocation.split('\') | where {$_}
	         $buildNameIndex = $buildNameSegments.length - 1
	         $buildName = $buildNameSegments[$buildNameIndex]
	         $powerdelivery.buildName = "$($powerdelivery.scriptName) - $($powerdelivery.environment)_$($now)"
	         $scriptParams["Build Name"] = $powerdelivery.buildName
	     }

        ReplaceReferencedConfigSettings($powerdelivery.config)
		
		$scriptParams["Drop Location"] = $powerdelivery.dropLocation
		
        Write-Host
        $scriptParams.Keys | % {
            "{0}: {1}" -f $_, $scriptParams[$_]
        }
        Write-Host

		Write-ConsoleSpacer
	    "= Environment"
	    Write-ConsoleSpacer
		Write-Host
        
        $configMessage = ""

        $powerdelivery.config.Keys | % {
			$configKey = $_
			$configVal = $powerdelivery.config[$_]
			
			$configMessage += "`n$($configKey): "
			
			if ($configVal.GetType().Name -eq 'Hashtable') {
				$configMessage += "{"
				$configSectionNames = @()
				$configVal.Keys | % { $configSectionNames += $_	}
				$configMessage += $configSectionNames -join ", "
				$configMessage += "}"
			}
			else {
				if ($_ -contains "password") {
					$configMessage += "*******"
				}
				else {
					$configMessage += $configVal
				}
			}
		}

		$yamlContents = PrintConfiguration -configNodes $powerdelivery.config -depth 0 -forYaml $true
		$yamlContents | Out-File -FilePath (Join-Path $dropLocation "$($appScript).yml") -Encoding ASCII

        PrintConfiguration -configNodes $powerdelivery.config -depth 0 -forYaml $false

        Write-BuildSummaryMessage -name "Environment" -header "Environment Configuration" -message $configMessage

        $noModules = $true

		if ($powerdelivery.deliveryModules) {
			$deliveryModules = @()
			$powerdelivery.deliveryModules | ForEach-Object {

				if ($noModules) {								
					Write-ConsoleSpacer
					"= Delivery Modules"
					Write-ConsoleSpacer
					Write-Host
					$noModules = $false
				}

				$moduleVersion = $null
				try {
					$moduleVersion = Get-Module "$($_)DeliveryModule" | select version | ForEach-Object { $_.Version.ToString() }
				}
				catch { }
				$moduleString = $_
				if ($moduleVersion) {
					$moduleString = "$($_) ($moduleVersion)"
				}
				$deliveryModules += $moduleString
				Write-BuildSummaryMessage -name "DeliveryModules" -header "Delivery Modules"  -message $moduleString
			}
			$deliveryModules -join ", "
		}

		if ($powerdelivery.environment -ne "Commit" -and $powerdelivery.onServer -eq $true) {

			$groupName = "$appScript $environment Builders"

			$buildGroup = $null
			$permitted = $false
			
			$sidSearchFactor = [Microsoft.TeamFoundation.Server.SearchFactor]::Sid
			$accountNameSearchFactor = [Microsoft.TeamFoundation.Server.SearchFactor]::AccountName
			$expandedQueryMembership = [Microsoft.TeamFoundation.Server.QueryMembership]::Expanded
			
			$requestingIdentity = $powerdelivery.groupSecurity.ReadIdentity($accountNameSearchFactor, $powerdelivery.requestedBy, $expandedQueryMembership)
			
			$powerdelivery.appGroups | % {
                if (($_.AccountName.ToLower() -eq $groupName.ToLower()) -and $buildGroup -eq $null) {
					$buildGroup = $_
					$groupMembers = $powerdelivery.groupSecurity.ReadIdentities($sidSearchFactor, $buildGroup.Sid, $expandedQueryMembership)					
					foreach ($member in $groupMembers) {
						foreach ($memberSid in $member.Members) {
							if ($memberSid -eq $requestingIdentity.Sid) {
								$permitted = $true
							}
						}
					}
                }
            }
			
			if (!$buildGroup) {
                throw "TFS Security group '$groupName' not found for project '$teamProject'. This group must exist to verify the user requesting the build is a member."
            }
			
			if (!$permitted) {
				throw "User '$($powerdelivery.requestedBy)' who queued build must be a member of TFS Security group '$groupName' to build targeting the '$environment' environment."
			}
			
	        $powerdelivery.priorBuild = $powerdelivery.buildServer.GetBuild("vstfs:///Build/Build/$priorBuild")
			
			if ($powerdelivery.priorBuild -eq $null) {
				throw "Build to promote '$priorBuild' could not be found. Are you sure you specified the build number of a prior build?"
			}
			
			$priorBuildName = $powerdelivery.priorBuild.BuildDefinition.Name.ToLower()
			
			if (!$priorBuildName.StartsWith($appScript.ToLower())) {
				throw "Prior build '$priorBuildName' is for a different product. Please specify the build number of a prior build for the same product."
			}
			
			if ($environment -eq 'Production') {
				if (!$priorBuildName.EndsWith('- test')) {
					throw "Attempt to target production with a non-test build. Please specify the build number of a prior Test environment build to promote it to production."
				}
			}
			elseif (!$priorBuildName.EndsWith('- commit')) {
				throw "Attempt to promote a non-commit build. Please specify the build number of a prior Commit environment build to promote it into this environment."
			}
		}

		InvokePowerDeliveryBuildAction -condition $true -stage $powerdelivery.init -description "Initialization" -status "Initializing" -blockName "Init"
	    InvokePowerDeliveryBuildAction -condition ((($powerdelivery.environment -eq 'Commit') -and $onServer) -or $powerdelivery.environment -eq 'Local') -stage $powerdelivery.compile -description "Compilations" -status "Compiling" -blockName "Compile"
	    
        $destDropLocation = $powerdelivery.dropLocation.TrimEnd('\')
        $destCurrentLocation = $powerdelivery.currentLocation.Path.TrimEnd('\')

		if ($powerdelivery.environment -ne "Local" -and $powerdelivery.environment -ne "Commit" -and $powerdelivery.onServer) {
	        $priorBuildDrop = $powerdelivery.priorBuild.DropLocation

            "$logPrefix Cloning deployed assets from $priorBuildDrop to $destDropLocation"
            Copy-Robust $priorBuildDrop $destDropLocation -recurse
	    }
        
    	"$logPrefix Retrieving assets from $destDropLocation into deploy directory"
    	Copy-Robust $destDropLocation $($powerdelivery.deployDir) -recurse

		"$logPrefix Setting location to $($powerdelivery.deployDir)"
		Set-Location $powerdelivery.deployDir

		InvokePowerDeliveryBuildAction -condition ($powerdelivery.environment -eq 'Commit' -or $powerdelivery.environment -eq 'Local') -stage $powerdelivery.testUnits -description "Unit Tests" -status "Testing Units" -blockName "TestUnits"
	    InvokePowerDeliveryBuildAction -condition $true -stage $powerdelivery.deploy -description "Deployments" -status "Deploying" -blockName "Deploy"
	    InvokePowerDeliveryBuildAction -condition $true -stage $powerdelivery.testEnvironment -description "Environment Tests" -status "Testing Environment" -blockName "TestEnvironment"
	    InvokePowerDeliveryBuildAction -condition ($environment -eq 'Commit' -or $environment -eq 'Local' -or $environment -eq 'Test') -stage $powerdelivery.testAcceptance -description "Acceptance Tests" -status "Testing Acceptance" -blockName "TestAcceptance"
	    InvokePowerDeliveryBuildAction -condition ($environment -eq 'CapacityTest') -stage $powerdelivery.testCapacity -description "Capacity Tests" -status "Testing Capacity" -blockName "TestCapacity"
        
	    Write-Host "`nPowerdelivery: Build succeeded!`n" -ForegroundColor DarkGreen
    }
    catch {
	    Set-Location $powerdelivery.currentLocation

		$ErrorRecord = $_[0]

 		Write-ConsoleSpacer
 		"= $logPrefix Build failure details"
 		Write-ConsoleSpacer

        $Exception = $ErrorRecord.Exception

 		if ($Exception -ne $null)
		{
 			"`nException(s) details:"
 			for ($i = 0; $Exception; $i++, ($Exception = $Exception.InnerException))
			{
                Write-Host

                if (![String]::IsNullOrWhiteSpace($Exception.Message)) {
 				    $Exception.Message
                }

 				$Exception.GetType().FullName

                if (![String]::IsNullOrWhiteSpace($Exception.StackTrace)) {
 				    $Exception.StackTrace
                }
			}
            Write-Host
		}


        if (![String]::IsNullOrWhiteSpace($ErrorRecord.PSMessageDetails)) {
            $ErrorRecord.PSMessageDetails
        }

        if (![String]::IsNullOrWhiteSpace($ErrorRecord.FullyQualifiedErrorId)) {
            $ErrorRecord.FullyQualifiedErrorId
        }

 		$ErrorRecord.InvocationInfo.PositionMessage
 		$ErrorRecord.ScriptStackTrace

	    Write-Host "`nPowerdelivery: Build Failed!`n" -ForegroundColor Red
		throw
    }
	finally {
		Stop-Transcript
		Copy-Item -Force $TranscriptFile $powerdelivery.dropLocation | Out-Null
		Set-Location $powerdelivery.currentLocation
	}
}

<#
.Synopsis
Contains code that will execute during the Init stage of the delivery pipeline build script.

.Description
Contains code that will execute during the Init stage of the delivery pipeline build script.

.Parameter action
The block of script containing the code to execute.

.Example
Init { DoStuff() }
#>
function Init {
	[CmdletBinding()]
	param([Parameter(Position=0, Mandatory=1)][scriptblock] $action)
	
	$powerdelivery.init = $action
}

<#
.Synopsis
Contains code that will execute during the Compile stage of the delivery pipeline build script.

.Description
Contains code that will execute during the Compile stage of the delivery pipeline build script.

.Parameter action
The block of script containing the code to execute.

.Example
Compile { DoStuff() }
#>
function Compile {
	[CmdletBinding()]
	param([Parameter(Position=0, Mandatory=1)][scriptblock] $action)
	
	$powerdelivery.compile = $action
}

<#
.Synopsis
Contains code that will execute during the Deploy stage of the delivery pipeline build script.

.Description
Contains code that will execute during the Deploy stage of the delivery pipeline build script.

.Parameter action
The block of script containing the code to execute.

.Example
Deploy { DoStuff() }
#>
function Deploy {
	[CmdletBinding()]
	param([Parameter(Position=0, Mandatory=1)][scriptblock] $action)
	
	$powerdelivery.deploy = $action
}

<#
.Synopsis
Contains code that will execute during the TestEnvironment stage of the delivery pipeline build script.

.Description
Contains code that will execute during the TestEnvironment stage of the delivery pipeline build script.

.Parameter action
The block of script containing the code to execute.

.Example
TestEnvironment { DoStuff() }
#>
function TestEnvironment {
	[CmdletBinding()]
	param([Parameter(Position=0, Mandatory=1)][scriptblock] $action)
	
	$powerdelivery.testEnvironment = $action
}

<#
.Synopsis
Contains code that will execute during the TestUnits stage of the delivery pipeline build script.

.Description
Contains code that will execute during the TestUnits stage of the delivery pipeline build script.

.Parameter action
The block of script containing the code to execute.

.Example
TestUnits { DoStuff() }
#>
function TestUnits {
	[CmdletBinding()]
	param([Parameter(Position=0, Mandatory=1)][scriptblock] $action)
	
	$powerdelivery.testUnits = $action
}

<#
.Synopsis
Contains code that will execute during the TestAcceptance stage of the delivery pipeline build script.

.Description
Contains code that will execute during the TestAcceptance stage of the delivery pipeline build script.

.Parameter action
The block of script containing the code to execute.

.Example
TestAcceptance { DoStuff() }
#>
function TestAcceptance {
	[CmdletBinding()]
	param([Parameter(Position=0, Mandatory=1)][scriptblock] $action)
	
	$powerdelivery.testAcceptance = $action
}

<#
.Synopsis
Contains code that will execute during the TestCapacity stage of the delivery pipeline build script.

.Description
Contains code that will execute during the TestCapacity stage of the delivery pipeline build script.

.Parameter action
The block of script containing the code to execute.

.Example
TestCapacity { DoStuff() }
#>
function TestCapacity {
	[CmdletBinding()]
	param([Parameter(Position=0, Mandatory=1)][scriptblock] $action)
	
	$powerdelivery.testCapacity = $action
}

<#
.Synopsis
Declares a continous delivery pipeline at the top of a powerdelivery build script.

.Description
Declares a continous delivery pipeline at the top of a powerdelivery build script.

.Parameter scriptName
The name of the script being executed. Should match the .ps1 filename (without extension).

.Parameter version
The version of the product being delivered. Should include 3 version specifiers (e.g. 1.0.5)

.Example
Pipeline "MyApp" -Version "1.0.5"
#>
function Pipeline {
	[CmdletBinding()]
	param(
		[Parameter(Position=0, Mandatory=1)][string] $scriptName,
		[Parameter(Mandatory=1)][string] $version
	)
	
	$powerdelivery.pipeline = $this
	$powerdelivery.buildAssemblyVersion = $version
	$powerdelivery.scriptName = $scriptName

	$buildAppVersion = "$appVersion"
	
    if ($environment -ne 'local') {
    	if ($onServer) {
	    	$changeSetNumber = $powerdelivery.changeSet.Substring(1)
	    	$buildAppVersion = "$($version).$($changeSetNumber)"
		}
		else {
			$changeSetNumber = "0"
			$buildAppVersion = "$($version).0"
		}
    }
	else {
		$buildAppVersion = $version
	}

    $powerdelivery.buildAppVersion = $buildAppVersion
}
tools\PowerDelivery\Invoke-Roundhouse.ps1
<#
.Synopsis
Migrates a database using roundhouse.exe.

.Description
The Invoke-Roundhouse cmdlet will run migration scripts on a database using the RoundhousE database migration tool.

In the Compile function of your build, you should copy the directory containing your roundhouse scripts to a subdirectory of the drop location named "Databases". For example, if you had a database named "MyDatabase" you'd have the following directory in TFS with your scripts:

$/MyProject/Databases/MyDatabase

You should copy them in Compile to here:

\DropLocation\Databases\MyDatabase

where DropLocation above is the result of the Get-BuildDropLocation function.

IMPORTANT: Call Invoke-Roundhouse only in the Deploy block.

.Example
Init {
  $script:dbServer = Get-BuildSetting DatabaseServer
  $script:dbName   = Get-BuildSetting DatabaseName

  $script:dbDir     = Join-Path $currentDirectory Databases
  $script:dbDropDir = Join-Path $dropLocation Databases

  $script:productionBackup = D:\Backups\MyDatabase_Latest.mdf
  $script:dataDir = "C:\Program Files\Microsoft SQL Server10\MSAS11.MSSQLSERVER\MSSQL\Data"
}

Compile {
  copy -Recurse -Filter *.* $dbDir $dropLocation
}

Deploy {
  Invoke-Roundhouse -server $dbServer `
                    -database $dbName `
                    -scriptsDir "$dbDropDir\MyDatabase" `
                    -restorePath $productionBackup `
                    -restoreOptions "MOVE 'MyDatabase' TO '$($dataDir)\$($dbName).mdf', MOVE 'MyDatabase_log' TO '$($dataDir)\$($dbName).ldf', REPLACE, RECOVERY"
}

.Parameter scriptsDir
Path to the directory containing Roundhouse migration scripts to run. Should be a subdirectory of your build's drop location.

.Parameter database
The name of the database to run scripts against.

.Parameter server
Optional. The name of the SQL server to run scripts against. Use this or the connectionString parameter.

.Parameter connectionString
Optional. The connection string to the database. Use this or the server parameter.

.Parameter restorePath
Optional. Path to a .mdf file (backup) of a database file to restore. Until you have a database in production don't specify this property in your build. Once you have a database in production, if you specify the path to your latest production backup file, this be restored prior to running migration scripts. This allows you to test the changes exactly as they would be applied were the current build released to production.

.Parameter restoreOptions
Optional. A string of options to pass to the RESTORE T-SQL statement performed. Use this to specify for instance the .sql and .log file paths that should be used instead of the ones contained within the backup file.

.Parameter commandTimeout
Optional. Default is 60. The number of seconds after which the deployment will timeout.

.Parameter databaseType
Optional. Default is 'sqlserver'. The type of database being deployed to.

.Parameter versionFile
Optional. Default is '_BuildInfo.xml'. This file is only needed if using an XML file to manage versions.

.Parameter doNotCreateDatabase
Optional. Default is false. Whether the database should not be created if it doesn't exist.

.Parameter disableOutput
Optional. Default is false. Whether output should not be displayed in the console.

.Parameter withTransaction
Optional. Default is false. Whether deployment should occur within a transaction.

.Parameter recoveryMode
Optional. Default is 'NoChange'. The mode of recovery used if deployment fails.
#>
function Invoke-Roundhouse {
    [CmdletBinding()]
    param(
      [Parameter(Position=0,Mandatory=1)][string] $scriptsDir, 
      [Parameter(Position=1,Mandatory=0)][string] $database, 
      [Parameter(Position=2,Mandatory=0)][string] $server, 
      [Parameter(Position=3,Mandatory=0)][string] $connectionString,
      [Parameter(Position=4,Mandatory=0)][string] $restorePath, 
      [Parameter(Position=5,Mandatory=0)][string] $restoreOptions,
      [Parameter(Position=6,Mandatory=0)][int] $commandTimeout = 60,
      [Parameter(Position=7,Mandatory=0)][string] $databaseType = 'sqlserver',
      [Parameter(Position=8,Mandatory=0)][string] $versionFile = '_BuildInfo.xml',
      [Parameter(Position=10,Mandatory=0)][switch] $doNotCreateDatabase = $false,
      [Parameter(Position=11,Mandatory=0)][switch] $disableOutput = $false,
      [Parameter(Position=10,Mandatory=0)][switch] $withTransaction = $false,
      [Parameter(Position=10,Mandatory=0)][string] $recoveryMode = 'NoChange'
    )

    Set-Location $powerdelivery.deployDir

    $logPrefix = "Invoke-Roundhouse:"

    $environment = Get-BuildEnvironment
    $dropLocation = Get-BuildDropLocation
    
    $dropScriptsDir = Join-Path $dropLocation $scriptsDir
    $localScriptsDir = Join-Path (gl) $scriptsDir
    $localOutDir = Join-Path $localScriptsDir output
    $dropOutDir = Join-Path $dropScriptsDir output

    $command = "rh --silent --commandtimeout=$commandTimeout --databasetype=`"$databaseType`" --versionfile=`"$versionFile`" --recoverymode=`"$recoveryMode`""
    
    if ($doNotCreateDatabase -eq $true) {
        $command += " --dc"
    }
    
    if ($disableOutput -eq $true) {
        $command += " --disableoutput"
    }
    
    if ($withTransaction -eq $true) {
        $command += " --withtransaction"
    }
    
    if ($debug -eq $true) {
        $command += " --debug"
    }
    
    if (![String]::IsNullOrWhiteSpace($server) -and ![String]::IsNullOrWhiteSpace($database)) {
        $command += " /s=$server /d=`"$database`""
    }
    elseif (![String]::IsNullOrWhiteSpace($connectionString)) {
        $command += " /c=`"$connectionString`""
    }
    else {
        throw "You must specify the server and database, or connectionString parameter."
    }
    
    $command += " /f=""$localScriptsDir"" /env=$environment /o=""$localScriptsDir\output"""
    
    if ($environment -ne 'Production' -and ![String]::IsNullOrWhitespace($restorePath)) {
        $command += " --restore --restorefrompath=`"$restorePath`""
        if (![String]::IsNullOrWhiteSpace($restoreOptions)) {
            $command += " --restorecustomoptions=`"$restoreOptions`""
        }
    }
    
    Write-Host "$logPrefix $command"

      Exec -ErrorAction Stop { 
          Invoke-Expression -Command $command       
      }

    Copy-Robust $localOutDir $dropOutDir -recurse
        
    if ([String]::IsNullOrWhiteSpace($connectionString)) {
        Write-BuildSummaryMessage -name "Deploy" -header "Deployments" -message "Roundhouse: $scriptsDir -> $database ($server)"
    }
    else {
        Write-BuildSummaryMessage -name "Deploy" -header "Deployments" -message "Roundhouse: $scriptsDir -> `"$connectionString`""
    }
}
tools\PowerDelivery\Invoke-SSISPackage.ps1
<#
.Synopsis
Runs an SSIS package using dtexec.exe.

.Description
The Invoke-SSIS cmdlet is used to execute a Microsoft SQL Server Integration Services (SSIS) package. This cmdlet runs dtexec.exe on a remote computer.

Copy your .dtsx packages to a UNC share within the Deploy block of your script onto each computer you wish to run packages on.

.Parameter package
A path local to the remote server the package is being executed on. If you had a UNC share on that server "\\MyServer\MyShare" and it was mapped to "D:\Somepath", use "D:\Somepath" here.

.Parameter computerName
The computer name(s) onto which to execute the package. If not "localhost", this computer must have PowerShell 3.0 with WinRM installed, allow execution of commands from the TFS build server and the account under which the build agent service is running.

.Parameter dtExecPath
The path to dtexec.exe on the server to run the command.

.Parameter credentialUserName
The username of the credentials to use for running dtexec. These credentials should have already been added to the build using the Export-BuildCredentials cmdlet. If you don't pass this the credentials of the currently logged in user will be loaded.

.Parameter packageArgs
Optional. A PowerShell hash containing name/value pairs to set as package arguments to dtexec.

.Example
Invoke-SSIS -package MyPackage.dtsx -ComputerName MyServer -packageArgs @{MyCustomArg = SomeValue}
#>
function Invoke-SSISPackage {
    [CmdletBinding()]
    param(
        [Parameter(Position=0,Mandatory=1)][string] $package, 
        [Parameter(Position=1,Mandatory=1)][string] $computerName, 
        [Parameter(Position=2,Mandatory=1)][string] $dtExecPath, 
        [Parameter(Position=3,Mandatory=0)][string] $credentialUserName,
        [Parameter(Position=4,Mandatory=0)][string] $packageArgs
    )
    
    Set-Location $powerdelivery.deployDir
    
    $logPrefix = "Invoke-SSISPackage:"

    $computerNames = $computerName -split "," | % { $_.Trim() }

    $dropLocation = Get-BuildDropLocation

    $logFileName = [System.IO.Path]::GetFileNameWithoutExtension("$package") + ".log"

    $dropLogPath = [System.IO.Path]::GetDirectoryName("$package")
    if (!(Test-Path "$dropLogPath")) {
        New-Item -ItemType Directory -Path $dropLogPath | Out-Null
    }

    foreach ($curComputerName in $computerNames) {

        # Allow credentials to travel from remote computer to TFS server
        #
        $dropUri = New-Object -TypeName System.Uri -ArgumentList $dropLocation
        if ($dropUri.IsUnc) {
            $dropHost = $dropUri.Host            
            $remoteComputer = [System.Net.Dns]::GetHostByName("$dropHost").HostName
            Add-RemoteCredSSPTrustedHost $curComputerName $remoteComputer
        }
        
        $remoteLogFile = Join-Path (New-RemoteTempPath $curComputerName $package) $logFileName

        $invokeArgs = @{
            "ArgumentList" = @($logPrefix, $package, $dtExecPath, $packageArgs, $dropLogPath, $remoteLogFile);
            "ScriptBlock" = {
                param($logPrefix, $package, $dtExecPath, $packageArgs, $dropLogPath, $remoteLogFile)

                # Delete the prior temporary log file if one exists
                #
                if (Test-Path -Path "$remoteLogFile") {
                    Remove-Item -Path "$remoteLogFile" -Force | Out-Null
                }

                $innerPackage = $package
                $innerDTExecPath = $dtExecPath
                $innerPackageArgs = $packageArgs

                $packageExecStatement = """$innerDTExecPath"" /File '$innerPackage'"
            
                if ($innerPackageArgs) {
                    $packageExecStatment += " $innerPackageArgs"
                }

                $packageExecStatement +=  " | Out-File ""$remoteLogFile"""

                Write-Host "$varLogPrefix $packageExecStatement"
                Invoke-Expression "& $packageExecStatement"

                # Copy the SSIS log file from the temporary directory to the build drop location.
                #
                if (Test-Path $remoteLogFile) {
                    if ([System.IO.Path]::GetDirectoryName("$remoteLogFile") -ne $dropLogPath) {
                        Write-Host "$varLogPrefix $remoteLogFile -> $dropLogPath"
                        Copy-Item "$remoteLogFile" "$dropLogPath"
                    }
                }
            };
            "ErrorAction" = "Stop"
        }

        Add-CommandCredSSP $curComputerName $invokeArgs $credentialUserName

        Invoke-Command @invokeArgs

        Write-BuildSummaryMessage -name "Deploy" -header "Deployments" -message "SSIS: $package ($curComputerName)"
    }
}
tools\PowerDelivery\New-RemoteShare.ps1
<#
.Synopsis
Creates a remote UNC share.

.Description
The New-RemoteShare cmdlet will create a UNC share on the remote computer if it does not exist.

.Parameter computerName
The computer to create the share on.

.Parameter shareName
The name to give the new share.

.Parameter shareDirectory
The local path on the remote computer to share.

.Example
New-RemoteShare REMOTE_COMPUTER MyShare "C:\MyShareDir"
#>
function New-RemoteShare {
    [CmdletBinding()]
    param (
        [Parameter(Position=0,Mandatory=1)] $computerName,
        [Parameter(Position=1,Mandatory=1)][string] $shareName, 
        [Parameter(Position=2,Mandatory=1)][string] $shareDirectory
    )
	
    # //************************************************************************************************************
    # // ***** Script Header *****
    # //
    # // Solution:  Coretech Share Functions
    # // File:      NewShareWithPermission.ps1
    # // Author:    Jakob Gottlieb Svendsen, Coretech A/S. http://blog.coretech.dk
    # // Purpose:   
    # // New-Share: Creates new Share on local or remote PC, with custom permissions.
    # // Required Parameters: FolderPath, ShareName
    # //
    # // New-ACE: Creates ACE Objects, for use when running New-Share.
    # // Required Parameters: Name, Domain
    # //
    # // New-SecurityDescriptor: used by New-Share to prepare the permissions.
    # // Required Parameters: ACEs
    #//
    # // Usage Examples:  
    # // New-Share -FolderPath "C:\Temp" -ShareName "Temp" -ACEs $ACE,$ACE2  -Description "Test Description" -Computer "localhost"
    # // Sharing of folder C:\Temp, with the Name "Temp". ACE's (Permissions) are sent via the -ACEs parameter.
    # // Create them with New-ACE and send one  or more, seperated by comma (or create and array and use that)
    # // 
    # // This is the first in a couple of share-administration scripts i am planning to make and release on the blog.
    # //
    # // Please comment the blog post, if you have any suggestions, questions or feedback.
    # // Contact me if you need us to make a custom script (or cause not for free ;-) ) 
    # //
    # // CORETECH A/S History:
    # // 0.0.1     JGS 30/06/2009  Created initial version.
    # //
    # // Customer History:
    # //
    # // ***** End Header *****
    # //**************************************************************************************************************
    #//----------------------------------------------------------------------------
    #//  Procedures
    #//----------------------------------------------------------------------------
    Function New-SecurityDescriptor (
        $ACEs = (throw "Missing one or more Trustees"), 
        [string] $ComputerName = ".")
    {
        #Create SeCDesc object
        $SecDesc = ([WMIClass] "\\$ComputerName\root\cimv2:Win32_SecurityDescriptor").CreateInstance()
        #Check if input is an array or not.
        if ($ACEs -is [System.Array])
        {
            #Add Each ACE from the ACE array
            foreach ($ACE in $ACEs )
            {
                $SecDesc.DACL += $ACE.psobject.baseobject
            }
        }
        else
        {
            #Add the ACE 
            $SecDesc.DACL =  $ACEs
        }
        #Return the security Descriptor
        return $SecDesc
    }
    Function New-ACE (
        [string] $Name = (throw "Please provide user/group name for trustee"),
        [string] $Domain = (throw "Please provide Domain name for trustee"), 
        [string] $Permission = "Read",
        [string] $ComputerName = ".",
        [switch] $Group = $false)
    {
        #Create the Trusteee Object
        $Trustee = ([WMIClass] "\\$ComputerName\root\cimv2:Win32_Trustee").CreateInstance()
        #Search for the user or group, depending on the -Group switch
        if (!$group)
        { $account = [WMI] "\\$ComputerName\root\cimv2:Win32_Account.Name='$Name',Domain='$Domain'" }
        else
        { $account = [WMI] "\\$ComputerName\root\cimv2:Win32_Group.Name='$Name',Domain='$Domain'" }
        #Get the SID for the found account.
        $accountSID = [WMI] "\\$ComputerName\root\cimv2:Win32_SID.SID='$($account.sid)'"
        #Setup Trusteee object
        $Trustee.Domain = $Domain
        $Trustee.Name = $Name
        $Trustee.SID = $accountSID.BinaryRepresentation
        #Create ACE (Access Control List) object.
        $ACE = ([WMIClass] "\\$ComputerName\root\cimv2:Win32_ACE").CreateInstance()
        #Select the AccessMask depending on the -Permission parameter
        switch ($Permission)
        {
            "Read"       { $ACE.AccessMask = 1179817 }
            "Change"  { $ACE.AccessMask = 1245631 }
            "Full"         { $ACE.AccessMask = 2032127 }
            default { throw "$Permission is not a supported permission value. Possible values are 'Read','Change','Full'" }
        }
        #Setup the rest of the ACE.
        $ACE.AceFlags = 3
        $ACE.AceType = 0
        $ACE.Trustee = $Trustee
        #Return the ACE
        return $ACE
    }
    Function New-Share (
        [string] $FolderPath = (throw "Please provide the share folder path (FolderPath)"),
        [string] $ShareName = (throw "Please provide the Share Name"), 
        $ACEs, 
        [string] $Description = "",
        [string] $ComputerName=".")
    {
        #Start the Text for the message.
        $text = "$ShareName ($FolderPath): "

        #Package the SecurityDescriptor via the New-SecurityDescriptor Function.
        $SecDesc = New-SecurityDescriptor $ACEs

        #Create the share via WMI, get the return code and create the return message.
        $Share = [WMICLASS] "\\$ComputerName\Root\Cimv2:Win32_Share"
        $result = $Share.Create("$FolderPath", "$ShareName", 0, 16777216, $Description, $null, $SecDesc)
        switch ($result.ReturnValue)
        {
            0 {$text += "has been success fully created" }
            2 {$text += "Error 2: Access Denied" }
            8 {$text += "Error 8: Unknown Failure" }
            9 {$text += "Error 9: Invalid Name"}
            10 {$text += "Error 10: Invalid Level" }
            21 {$text += "Error 21: Invalid Parameter" }
            22 {$text += "Error 22 : Duplicate Share"}
            23 {$text += "Error 23: Redirected Path" }
            24 {$text += "Error 24: Unknown Device or Directory" }
            25 {$text += "Error 25: Net Name Not Found" }
        }

        #Create Custom return object and Add results
        $return = New-Object System.Object
        $return | Add-Member -type NoteProperty -name ReturnCode -value $result.ReturnValue
        $return | Add-Member -type NoteProperty -name Message -value $text  

        #Return result object
        $return
    }
    #//----------------------------------------------------------------------------
    #//  End Script
    #//----------------------------------------------------------------------------

	Set-Location $powerdelivery.deployDir

    $logPrefix = "New-RemoteShare:"

    $buildAccountName = whoami

    $computerNames = $computerName -split "," | % { $_.Trim() }

    foreach ($curComputerName in $computerNames) {

        if (!((net view "\\$curComputerName") -match "^$shareName")) {

            Invoke-Command -ComputerName $curComputerName {
        
                if (!(Test-Path $using:shareDirectory)) {
                    "$using:logPrefix Creating directory $using:shareDirectory on $using:curComputerName..."
                    mkdir $using:shareDirectory -Force | Out-Null
                }
            }

            "$logPrefix Creating UNC share \\$curComputerName\$shareName for $shareDirectory..."

            $result = New-Share -FolderPath "$shareDirectory" -ShareName "$shareName" -ComputerName "$curComputerName"

            if ($result.ReturnCode -ne 0) {
                throw "Error creating share \\$curComputerName\$shareName, error code was $result.ReturnCode"
            }
        }

        Invoke-Command -ComputerName $curComputerName {

            $user = New-Object System.Security.Principal.NTAccount($using:buildAccountName)
            $strSID = $user.Translate([System.Security.Principal.SecurityIdentifier])
            $sid = New-Object System.Security.Principal.SecurityIdentifier($strSID) 
            [byte[]]$ba = ,0 * $sid.BinaryLength
            [void]$sid.GetBinaryForm($ba,0)

            $trustee = ([WMIClass] "Win32_Trustee").CreateInstance() 
            $trustee.SID = $ba
        
            $ace = ([WMIClass] "Win32_ace").CreateInstance() 
            $ace.AccessMask = 2032127
            $ace.AceFlags = 3
            $ace.AceType = 0
            $ace.Trustee = $trustee 

            $wPrivilege = gwmi Win32_LogicalShareSecuritySetting -filter "name=`"$using:shareName`"" 
            $wPrivilege.psbase.Scope.Options.EnablePrivileges = $true 
            $oldDACL = ($wPrivilege.GetSecurityDescriptor()).Descriptor.DACL 
            $sd = ([WMIClass] "Win32_SecurityDescriptor").CreateInstance() 
            $sd.DACL = $oldDACL
            $sd.DACL += @($ace.psobject.baseobject)
            $sd.ControlFlags = "0x4"
            $wPrivilege.SetSecurityDescriptor($sd) | Out-Null
        }
    }
}
tools\PowerDelivery\New-RemoteTempPath.ps1
function New-RemoteTempPath {
    [CmdletBinding()]
    param(
        [Parameter(Position=0,Mandatory=1)][string] $computerName,
        [Parameter(Position=1,Mandatory=1)][string] $path
    )

    $logPrefix = "New-RemoteTempPath:"

    $invokeArgs = @{
        "ComputerName" = $computerName;
        "ArgumentList" = @($logPrefix, $path);
        "ScriptBlock" = {
            param($logPrefix, $path)

            $newTempDir = $path
            $newTempDirUri = New-Object -TypeName System.Uri -ArgumentList $newTempDir

            if ($newTempDirUri.IsUnc) {
                throw "$newTempDir is a UNC path and cannot be created as a temporary directory on a remote computer."
            }

            if (![System.IO.Path]::IsPathRooted("$path")) {
                $tempOutputDirectory = Join-Path $env:TEMP "PowerDelivery"
                $tempPath = [System.IO.Path]::GetDirectoryName("$path")
                if ($tempPath) {
                    $newTempDir = Join-Path "$tempOutputDirectory" "$tempPath"
                }
            }
            else {
                $newTempDir = [System.IO.Path]::GetDirectoryName("$path")
            }

            if (!(Test-Path $newTempDir)) {
                Write-Host "$logPrefix Creating $newTempDir on $($env:COMPUTERNAME)"
                New-Item -ItemType Directory -Path $newTempDir | Out-Null
            }
                
            return $newTempDir
        };
        "ErrorAction" = "Stop"
    }

    return Invoke-Command @invokeArgs
}
tools\PowerDelivery\New-WindowsUserAccount.ps1
<#
.Synopsis
Creates a local Windows user account on a computer.

.Description
Creates a local Windows user account on a computer.

.Parameter userName
The username of the account to create.

.Parameter password
The password of the account to create.

.Parameter computerName
Optional. The computer to be modified.

.Example
Add-WindowsUserToGroup -userName 'DOMAIN\MyUser' `
					   -password 's@m3p@ss' `
					   -computerName MYCOMPUTER
#>
function New-WindowsUserAccount {
	[CmdletBinding()]
	param(
		[Parameter(Position=0,Mandatory=1)] $userName,
		[Parameter(Position=1,Mandatory=1)] $password,
		[Parameter(Position=2,Mandatory=0)] $computerName
	)
	
	Set-Location $powerdelivery.deployDir

    $logPrefix = "New-WindowsUserAccount:"

    $computerNames = $computerName -split "," | % { $_.Trim() }

    foreach ($curComputerName in $computerNames) {

        Invoke-Command -ComputerName $curComputerName {

		    $localUsersSet = [ADSI]"WinNT://$using:curComputerName/Users"
		    $localUsers = @($localUsersSet.psbase.Invoke("Members")) 

		    $foundAccount = $false

		    $localUsers | foreach {
			    if ($_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null) -eq $using:userName) {
				    $foundAccount = $true
			    }
		    }

		    if (!$foundAccount) {
			    Write-Host "$using:logPrefix Adding $using:userName user to $($using:curComputerName)..."

			    $addUserArgs = @(
				    "user",
				    $using:userName,
				    $using:password,
				    "/add",
				    "/active:YES",
				    "/expires:NEVER",
				    "/FullName:`"$using:userName`""
			    )

			    $addUserProcess = Start-Process -FilePath "C:\Windows\System32\net.exe" -ArgumentList $addUserArgs -PassThru -Wait
			    $addUserProcess.WaitForExit()

			    "$using:logPrefix User $using:userName created on $using:curComputerName successfully."
		    }
	    }
    }
}
tools\PowerDelivery\PowerDelivery.psd1
 
tools\PowerDelivery\PowerDelivery.psm1
<#
PowerDelivery.psm1

powerdelivery - http://eavonius.github.com/powerdelivery

PowerShell module that enables writing build scripts that follow continuous delivery 
principles and deploy product assets into multiple environments.
#>

function Write-ConsoleSpacer() {
    "================================================================"
}

function Mount-IfUNC {
    [CmdletBinding()]
    param([Parameter(Mandatory=1)][string] $path)
	
	if ($path.StartsWith("\\")) {
		$uncPathWithoutBackslashes = $path.Substring(2)
		$pathSegments = $uncPathWithoutBackslashes -split "\\"
		$uncPath = "\\$($pathSegments[0])\$($pathSegments[1])"
	}
}

function Get-CurrentBuildDetail {

    $collectionUri = Get-BuildCollectionUri

    "Connecting to TFS server at $collectionUri..."

    $projectCollection = [Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory]::GetTeamProjectCollection($collectionUri)
    $buildServer = $projectCollection.GetService([Microsoft.TeamFoundation.Build.Client.IBuildServer])

    $buildUri = Get-BuildUri
    "Opening Information for Build $buildUri..."

    return $buildServer.GetBuild($buildUri)
}

function LoadTFS($vsVersion = "11.0") {

    $vsInstallDir = Get-ItemProperty -Path "Registry::HKEY_USERS\.DEFAULT\Software\Microsoft\VisualStudio\11.0_Config" -Name InstallDir -ErrorAction SilentlyContinue     
    if ([string]::IsNullOrWhiteSpace($vsInstallDir)) {
        $vsInstallDir = Get-ItemProperty -Path "Registry::HKEY_USERS\.DEFAULT\Software\Microsoft\VisualStudio\10.0_Config" -Name InstallDir -ErrorAction SilentlyContinue
        if ([string]::IsNullOrWhiteSpace($vsInstallDir)) {
            throw "No version of Visual Studio with the same tools as your version of TFS is installed on the build server."
        }
    }
	
    $ENV:Path += ";$($vsInstallDir.InstallDir)"

    $refAssemblies = "ReferenceAssemblies\v2.0"
    $privateAssemblies = "PrivateAssemblies"
    $tfsAssembly = Join-Path -Path $vsInstallDir.InstallDir -ChildPath "$refAssemblies\Microsoft.TeamFoundation.dll"
    $tfsClientAssembly = Join-Path -Path $vsInstallDir.InstallDir -ChildPath "$refAssemblies\Microsoft.TeamFoundation.Client.dll"
    $tfsBuildClientAssembly = Join-Path -Path $vsInstallDir.InstallDir -ChildPath "$refAssemblies\Microsoft.TeamFoundation.Build.Client.dll"
    $tfsBuildWorkflowAssembly = Join-Path -Path $vsInstallDir.InstallDir -ChildPath "$privateAssemblies\Microsoft.TeamFoundation.Build.Workflow.dll"
    $tfsVersionControlClientAssembly = Join-Path -Path $vsInstallDir.InstallDir -ChildPath "$refAssemblies\Microsoft.TeamFoundation.VersionControl.Client.dll"

    [Reflection.Assembly]::LoadFile($tfsClientAssembly) | Out-Null
    [Reflection.Assembly]::LoadFile($tfsBuildClientAssembly) | Out-Null
    [Reflection.Assembly]::LoadFile($tfsBuildWorkflowAssembly) | Out-Null
    [Reflection.Assembly]::LoadFile($tfsVersionControlClientAssembly) | Out-Null
}

function Require-NonNullField($variable, $errorMsg) {
	if ($variable -eq $null -or $variable -eq '') {
		throw $errorMsg;
	}
}

function Resolve-Error ($ErrorRecord=$Error[0])
{
   $ErrorRecord | Format-List * -Force
   $ErrorRecord.InvocationInfo |Format-List *
   $Exception = $ErrorRecord.Exception
   for ($i = 0; $Exception; $i++, ($Exception = $Exception.InnerException))
   {   "$i" * 80
       $Exception |Format-List * -Force
   }
}

$script:powerdelivery = @{}
$powerdelivery.build_success = $true
$powerdelivery.pipeline = $null

$scriptDir = Split-Path $MyInvocation.MyCommand.Path
gci $scriptDir -Filter "*.ps1" | ForEach-Object { . (Join-Path $scriptDir $_.Name) }
tools\PowerDelivery\Process-SSAS.ps1
function Process-SSAS {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=1)][string] $computerName, 
        [Parameter(Mandatory=1)][string] $cubeName,
        [Parameter(Mandatory=0)][string] $processType = "ProcessFull"
    )
    
    Set-Location $powerdelivery.deployDir

    $logPrefix = "Process-SSAS:"

    $computerNames = $computerName -split "," | % { $_.Trim() }

    foreach ($curComputerName in $computerNames) {

        $processCubeQuery = @"
        <Process xmlns=`"http://schemas.microsoft.com/analysisservices/2003/engine`">
          <Type>$processType</Type>
          <Object>
            <DatabaseID>$cubeName</DatabaseID>
          </Object>
        </Process>
"@
        "$logPrefix Processing $cubeName on $curComputerName..."

        $processResult = Invoke-Command -ComputerName $curComputerName {
            Invoke-ASCMD -server "$using:curComputerName" -query "$using:processCubeQuery"
        }

        [xml]$processXml = $processResult | ConvertTo-Xml

        $nsOut = new-object Xml.XmlNamespaceManager $processXml.NameTable
        $nsOut.AddNamespace('xa', 'urn:schemas-microsoft-com:xml-analysis')
        $nsOut.AddNamespace('xae', 'urn:schemas-microsoft-com:xml-analysis:empty')
        $nsOut.AddNamespace('xex', 'urn:schemas-microsoft-com:xml-analysis:exception')

        $warningNodes = $processXml.SelectNodes("//xa:return/xae:root/xex:Messages/xex:Warning", $nsOut)

        $deploySuccess = $true

        if ($warningNodes -ne $null) {
            foreach ($warningNode in $warningNodes) {
                
                $warningDesc = $warningNode.Description
                
                "SSAS Processing Error: $warningDesc"

                $deploySuccess = $false
            }
        }

        if ($deploySuccess -eq $false) {
            throw "One or more errors occurred deploying an SSAS cube. See the build log for details."
        }
        else {
            "$logPrefix Processing of $cubeName on $curComputerName successful."
        }
    }
}
tools\PowerDelivery\Publish-BuildAssets.ps1
<#
.Synopsis
Publishes build assets from the build working directory to the drop location.

.Description
Copies build assets from the build working directory to the remote UNC drop location. You should specify 
relative paths for this command.

.Parameter path
The relative local path of assets in the current directory that should be copied remotely.

.Parameter destination
The relative remote path to copy the assets to.

.Parameter filter
Optional. A filter for the file extensions that should be included.

.Example
Publish-BuildAssets "SomeDir\\SomeFiles" "SomeDir" -Filter *.*
#>
function Publish-BuildAssets {
	[CmdletBinding()]
	param(
		[Parameter(Position=0,Mandatory=1)][string] $path,
		[Parameter(Position=1,Mandatory=1)][string] $destination,
		[Parameter(Position=2,Mandatory=0)][string] $filter	= $null,
		[Parameter(Position=3,Mandatory=0)][switch] $recurse = $false
	)

    $logPrefix = "Publish-BuildAssets:"

	$currentDirectory = Get-Location
	$dropLocation = Get-BuildDropLocation
	
	$sourcePath = Join-Path $currentDirectory $path
	$destinationPath = Join-Path $dropLocation $destination
	
	mkdir -Force $destinationPath | Out-Null
	
	$copyArgs = @{"Force" = $true; "Filter" = $filter; "Path" = $sourcePath; "Destination" = $destinationPath}
	
    $message = "$logPrefix Copying $sourcePath to $destinationPath"

	if ($recurse) {
		$copyArgs.Add("Recurse", $true)
        $message += " recursively"
	}
	
    if (![String]::IsNullOrWhiteSpace($filter)) {
        $message += " with filter $filter"
    }

    Write-Host $message

	& copy @copyArgs
	
	Write-BuildSummaryMessage -name "Assets" -header "Published Assets" -message $path
}
tools\PowerDelivery\Publish-MasterDataServices.ps1
function Publish-MasterDataServices {
    param(
        [Parameter(Position=0,Mandatory=1)][string] $computerName,
        [Parameter(Position=1,Mandatory=1)][string] $package,
        [Parameter(Position=2,Mandatory=1)][string] $connectionString,
        [Parameter(Position=3,Mandatory=0)][string] $version,
        [Parameter(Position=4,Mandatory=0)][string] $credentialUserName,
        [Parameter(Position=5,Mandatory=0)][string] $mdsDeployPath = "C:\Program Files\Microsoft SQL Server\110\Master Data Services\Configuration\"
    )

    $logPrefix = "Publish-MasterDataServices:"

    $computerNames = $computerName -split "," | % { $_.Trim() }

    foreach ($curComputerName in $computerNames) {

        "$logPrefix Publishing $package to up Master Data Services on $computerName"

        $dropLocation = Get-BuildDropLocation

        # Allow credentials to travel from remote computer to TFS server
        #
        $dropUri = New-Object -TypeName System.Uri -ArgumentList $dropLocation
        
        if ($dropUri.IsUnc) {
            
            $dropHost = $dropUri.Host
            
            $remoteComputer = [System.Net.Dns]::GetHostByName("$dropHost").HostName

            Add-RemoteCredSSPTrustedHost $curComputerName $remoteComputer
        }

        $invokeArgs = @{
            "ArgumentList" = @($package, $version, $connectionString, $mdsDeployPath, $dropLocation, $logPrefix);
            "ScriptBlock" = {
                param($varPackage, $varVersion, $varConnectionString, $varMdsDeployPath, $varDropLocation, $varLogPrefix)

                $tempOutputDirectory = Join-Path $env:TEMP "PowerDelivery"
                
                # Create the subdirectory of the package path if one was specified
                #
                $packagePath = [System.IO.Path]::GetDirectoryName("$varPackage")
                if ($packagePath) {
                    $tempSubPath = Join-Path "$tempOutputDirectory" "$packagePath"
                    if (!(Test-Path -Path $tempSubPath)) {
                        New-Item -ItemType Directory -Path "$tempSubPath" | Out-Null
                    }
                }
                
                $tempPackageFile = Join-Path "$tempOutputDirectory" "$varPackage"

                # Delete the prior temporary package if one exists
                #
                if (Test-Path -Path "$tempPackageFile") {
                    Remove-Item -Path "$tempPackageFile" -Force | Out-Null
                }

                $remotePackage = Join-Path $varDropLocation $varPackage

                Copy-Item -Path $remotePackage -Destination $tempPackageFile

                # NOTE: The TFS Build Service account must have been given function and 
                # model permission to MDS so this command will succeed.
                #
                $mdsDeployPath = Join-Path $varMdsDeployPath "MDSModelDeploy"

                $mdsDeployCommand = """$mdsDeployPath"" deployupdate -package ""$tempPackageFile"""

                if ($varVersion) {
                    $mdsDeployCommand += " -version ""$varVersion"""
                }

                Write-Host "$varLogPrefix $mdsDeployCommand"
                Invoke-Expression "& $mdsDeployCommand"

                if ($LASTEXITCODE -ne $null -and $LASTEXITCODE -ne 0) {
                    throw "Error publishing Master Data Services, exit code was $LASTEXITCODE"
                }

                # Update MDS to validate any new data
                #
                $sqlConnection = New-Object System.Data.OleDb.OleDbConnection
                $sqlConnection.ConnectionString = $varConnectionString
                $sqlConnection.Open()

                $currentUserName = whoami
                
                $validateNewDataQuery = @"
DECLARE @UserId int
DECLARE @ModelId int
DECLARE @VersionId int
DECLARE @ModelName nvarchar(50)
DECLARE @ModelNames TABLE(RowID INT NOT NULL IDENTITY(1,1) primary key, ModelName nvarchar(50))
DECLARE @LastRowID int
DECLARE @RowID int

SELECT @UserId = ID FROM mdm.tblUser WHERE UserName = '$currentUserName'

INSERT INTO @ModelNames
SELECT Name FROM mdm.tblModel WHERE Name <> 'Metadata'
SELECT @RowID = MIN(RowID) FROM @ModelNames
SELECT @LastRowID = MAX(RowID) FROM @ModelNames

WHILE @RowID <= @LastRowID
BEGIN
    SELECT @ModelName = ModelName FROM @ModelNames WHERE RowID = @RowID
    SET @ModelId = (SELECT TOP 1 Model_ID FROM mdm.viw_SYSTEM_SCHEMA_MODELS WHERE Model_Name = @ModelName)
    SET @VersionId = (SELECT MAX(ID) FROM mdm.viw_SYSTEM_SCHEMA_VERSION WHERE Model_ID = @ModelId)
    EXEC mdm.udpValidateModel @UserId, @ModelId, @VersionId, 1
    SET @RowID = @RowID + 1
END
"@
                $validateNewDataCmd = New-Object System.Data.OleDb.OleDbCommand($validateNewDataQuery, $sqlConnection)
                $validateNewDataCmd.ExecuteNonQuery()
                
                $sqlConnection.Close()
            };
            "ErrorAction" = "Stop"
        }

        Add-CommandCredSSP $curComputerName $invokeArgs $credentialUserName

        Invoke-Command @invokeArgs

        Write-BuildSummaryMessage -name "Deploy" -header "Deployments" -message "Master Data Services: $package -> $computerName"
    }
}
tools\PowerDelivery\Publish-SSAS.ps1
<#
.Synopsis
Publishes a SQL Server Analysis Services (SSAS) cube.

.Description
The Publish-SSAS cmdlet will deploy a SQL analysis services .asdatabase file to a server. It creates a .XMLA 
file and replaces any connection strings with updated values before copying it to a remote share on the target 
SSAS server. After execution, a log file is available in the same directory.

.Parameter asDatabase
The .asdatabase file to deploy. Is a path local to machine specified by the computer parameter.

.Parameter computer
The computer(s) to deploy to.

.Parameter sqlVersion
Optional. The version of SQL to use. Default is "11.0"

.Parameter deploymentUtilityPath
Optional. The full path to the Microsoft.AnalysisServices.DeploymentUtility.exe command-line tool.

.Parameter cubeName
Optional. The name to deploy the cube as. Can only be omitted if only one cube (model) is included in the asdatabase package.

.Parameter connections
Optional. Hash of values that match the parameters of the Set-SSASConnection cmdlet.

.Parameter driveLetter
Optional. The drive letter on the target computer to deploy to.

.Example
Publish-SSAS -computer "MyServer" -tabularServer "MyServer\INSTANCE" -asDatabase "MyProject\bin\Debug\MyModel.asdatabase"
#>
function Publish-SSAS {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=1)][string] $asDatabase, 
        [Parameter(Mandatory=1)][string] $computer, 
        [Parameter(Mandatory=0)][string] $sqlVersion = '11.0',
        [Parameter(Mandatory=0)][string] $deploymentUtilityPath = "C:\Program Files (x86)\Microsoft SQL Server\110\Tools\Binn\ManagementStudio\Microsoft.AnalysisServices.Deployment.exe",
        [Parameter(Mandatory=0)][string] $cubeName,
        [Parameter(Mandatory=0)] $connections,
        [Parameter(Mandatory=0)][string] $driveLetter = $powerdelivery.deployDriveLetter
    )
    
    Set-Location $powerdelivery.deployDir

    $logPrefix = "Publish-SSAS:"

    $asModelName = [System.IO.Path]::GetFileNameWithoutExtension($asDatabase)
    $asFilesDir = [System.IO.Path]::GetDirectoryName($asDatabase)
    $xmlaPath = Join-Path -Path $asFilesDir -ChildPath "$($asModelName).xmla"

    $asDatabase = Join-Path -Path (Get-Location) -ChildPath $asDatabase

    $deployCommand = "& ""$deploymentUtilityPath"" ""$asDatabase"" ""/d"" ""/o:$xmlaPath"""

    "$logPrefix $deployCommand"

    Invoke-Expression $deployCommand

    Start-Sleep -Seconds 15

    $xmlaFullPath = Join-Path -Path (Get-Location) -ChildPath $xmlaPath

    [xml]$xmlaDoc = Get-Content $xmlaFullPath

    $ns = new-object Xml.XmlNamespaceManager $xmlaDoc.NameTable
    $ns.AddNamespace('xmla', 'http://schemas.microsoft.com/analysisservices/2003/engine')

    $batchNode = $xmlaDoc.SelectSingleNode("//xmla:Batch", $ns)
    $parallelNode = $batchNode.SelectSingleNode("xmla:Parallel", $ns)
    $batchNode.RemoveChild($parallelNode)

    $computerNames = $computer -split "," | % { $_.Trim() }

    foreach ($curComputerName in $computerNames) {
        
        $newModelName = $asModelName

        if (![String]::IsNullOrWhiteSpace($cubeName)) {

            $newModelName = $cubeName

            $objectNode = $xmlaDoc.SelectSingleNode("//xmla:Batch/xmla:Alter/xmla:Object", $ns)
            $objectNode.DatabaseID = $cubeName

            $databaseNode = $xmlaDoc.SelectSingleNode("//xmla:Batch/xmla:Alter/xmla:ObjectDefinition/xmla:Database", $ns)
            $databaseNode.ID = $cubeName
            $databaseNode.Name = $cubeName

            if ($connections -ne $null) {
        
                $connections.Keys | % {
                    $connection = $connections[$_]

                    $connectionStringNode = $databaseNode.SelectSingleNode("xmla:DataSources/xmla:DataSource[xmla:Name='$($connection.ConnectionName)']/xmla:ConnectionString", $ns)

                    if ($connectionStringNode -eq $null) {
                        throw "Unable to find connection named '$($connection.ConnectionName)' connection to update."
                    }

                    $connectionStringNode.'#text' = $connection.ConnectionString
                }
            }

            $xmlaDoc.Save($xmlaFullPath)
        }

        Deploy-BuildAssets -computerName $curComputerName -path $asFilesDir -destination $asFilesDir -DriveLetter $driveLetter

        $localDeployPath = Get-ComputerLocalDeployPath $curComputerName -DriveLetter $driveLetter
        $remoteDeployPath = Get-ComputerRemoteDeployPath $curComputerName -DriveLetter $driveLetter

        $xmlaLocalDeployPath = [System.IO.Path]::Combine($localDeployPath, $xmlaPath)
        $outLocalDeployPath = $xmlaLocalDeployPath -replace ".xmla", "ExecutionLog.xml"

        $remoteCommand = "Invoke-ASCMD -server ""$curComputerName"" -inputFile ""$xmlaLocalDeployPath"" | Out-File ""$outLocalDeployPath"""

        "$logPrefix $remoteCommand"

        Invoke-Expression "Invoke-Command -ComputerName ""$curComputerName"" -ScriptBlock { $remoteCommand }"

        $outRemoteDeployPath = Join-Path $remoteDeployPath ($xmlaPath -replace ".xmla", "ExecutionLog.xml")

        [xml]$outDoc = Get-Content $outRemoteDeployPath

        $nsOut = new-object Xml.XmlNamespaceManager $outDoc.NameTable
        $nsOut.AddNamespace('xa', 'urn:schemas-microsoft-com:xml-analysis')
        $nsOut.AddNamespace('xae', 'urn:schemas-microsoft-com:xml-analysis:empty')
        $nsOut.AddNamespace('xex', 'urn:schemas-microsoft-com:xml-analysis:exception')

        $warningNodes = $outDoc.SelectNodes("//xa:return/xae:root/xex:Messages/xex:Warning", $nsOut)

        $deploySuccess = $true

        if ($warningNodes -ne $null) {
            foreach ($warningNode in $warningNodes) {
                
                $warningDesc = $warningNode.Description
                
                "SSAS Deployment Error: $warningDesc"

                $deploySuccess = $false
            }
        }

        if ($deploySuccess) {
            Write-BuildSummaryMessage -name "Deploy" -header "Deployments" -message "SSAS: $($asModelName).asdatabase -> $newModelName ($curComputerName)"
        }
        else {
            throw "One or more errors occurred deploying an SSAS cube. See the build log for details."
        }
    }
}
tools\PowerDelivery\Publish-WebDeploy.ps1
function Publish-WebDeploy {
	param(
		[Parameter(Position=0,Mandatory=1)] $WebComputer,
		[Parameter(Position=1,Mandatory=1)] [string] $Package,
		[Parameter(Position=2,Mandatory=1)] [string] $WebSite,
		[Parameter(Position=3,Mandatory=1)] [int] $WebPort,
		[Parameter(Position=4,Mandatory=1)] [string] $WebURL,
		[Parameter(Position=5,Mandatory=1)] [string] $WebPassword,
		[Parameter(Position=6,Mandatory=0)] [Hashtable] $Parameters,
        [Parameter(Position=7,Mandatory=0)] [string] $AppPoolAccountUser,
        [Parameter(Position=8,Mandatory=0)] [string] $AppPoolAccountPassword,
		[Parameter(Position=9,Mandatory=0)] [string] $BringOffline = 'false',
		[Parameter(Position=10,Mandatory=0)] [string] $WebDeployDir = "C:\Program Files\IIS\Microsoft Web Deploy v3",
        [Parameter(Position=11,Mandatory=0)] $RuntimeVersion = 'v4.0'
	)
	
	Set-Location $powerdelivery.deployDir

    $logPrefix = "Publish-WebDeploy:"

    $computerNames = $WebComputer -split "," | % { $_.Trim() }
	
    if (([string]::IsNullOrWhiteSpace($AppPoolAccountUser) -and ![string]::IsNullOrWhiteSpace($AppPoolAccountPassword)) -or `
        (![string]::IsNullOrWhiteSpace($AppPoolAccountUser) -and [string]::IsNullOrWhiteSpace($AppPoolAccountPassword))) {
        throw "You must specify both the AppPool account username and password parameters to set the AppPool identity during web deployment"
    }

	foreach ($computerName in $computerNames) {
	
		$msDeployPath = Join-Path $WebDeployDir "msdeploy.exe"

		$invokeArgs = @{}
		
		$invokeArgs.Add('webComputer', $computerName)
		$invokeArgs.Add('webDeployDir', $WebDeployDir)
		$invokeArgs.Add('WebPort', $WebPort)
		$invokeArgs.Add('webSite', $WebSite)
		$invokeArgs.Add('webPassword', $WebPassword)
        $invokeArgs.Add('runtimeVersion', $RuntimeVersion)

		& Enable-WebDeploy @invokeArgs
		
	    $publishSettingsFile = "$(gl)\$($WebSite).publishsettings"

        Import-Snapin "wdeploysnapin3.0"

		if ($computerName -like 'localhost') {
			
			$computerName = $env:COMPUTERNAME
			$WebUrl = $WebUrl.Replace("localhost", $env:COMPUTERNAME)
			
            Import-Snapin WebAdministration

			if (!(Test-Path "IIS:\AppPools\$($WebSite)"))
			{
				$appPoolObj = New-WebAppPool -Name $WebSite -Force
			}
			
			$webSiteObj = Get-Website -Name $WebSite
			if (!$webSiteObj) {
				"Creating $($WebSite) on $($computerName)..."
				$sitePath = "C:\inetpub\$($WebSite)"
				mkdir $sitePath -Force | Out-Null
				$webSiteObj = New-Website -Name $WebSite -ApplicationPool $WebSite -Force -PhysicalPath $sitePath -Port $WebPort
			}
			
			if ($Parameters -ne $null) {
				foreach ($deploymentParamKey in $Parameters.Keys) {
					$deploymentParamVal = $Parameters[$deploymentParamKey]
					if ($deploymentParamVal -like 'localhost') {
						$Parameters[$deploymentParamKey] = $env:COMPUTERNAME
					}
				}
			}
		}

	    New-WDPublishSettings -ComputerName $computerName -Site $WebSite `
	                          -SiteUrl $WebURL -FileName $publishSettingsFile `
	                          -AllowUntrusted -AgentType MSDepSvc | Out-Null

		try {

			if ($BringOffline) {
				if ($BringOffline -eq 'true') {

					$deleteOfflineFile = "& `"$msDeployPath`" -verb:delete -dest:`"contentPath=$($WebSite)/App_Offline.htm,computername=$($computerName)`""
					
					$deleteOfflineFileResult = Invoke-Command -ComputerName $computerName -ErrorAction SilentlyContinue {
                        "$using:logPrefix $using:offlineCmd"
						iex $using:deleteOfflineFile
					}
										
					$offlineCmd = "& `"$msDeployPath`" -verb:sync -source:iisApp=`"$($WebSite)`" -dest:`"auto,computername=$($computerName)`" -enableRule:AppOffline -enableRule:DoNotDeleteRule"
					
					Invoke-Command -ComputerName $computerName -ErrorAction Stop {
                        "$using:logPrefix $using:offlineCmd"
						iex $using:offlineCmd
					}
				}
			}
		
			Restore-WDPackage -Package $Package `
							-DestinationPublishSettings $publishSettingsFile `
							-Parameters $Parameters `
							-ErrorAction Stop

            if (![String]::IsNullOrWhiteSpace($AppPoolAccountUser)) {
            
                New-WindowsUserAccount -userName $AppPoolAccountUser -password $AppPoolAccountPassword -computerName $computerName
                Add-WindowsUserToGroup -userName $AppPoolAccountUser -groupName "Performance Monitor Users" -computerName $computerName
                Set-AppPoolIdentity -appPoolName $WebSite -userName $AppPoolAccountUser -password $AppPoolAccountPassword -computerName $computerName
            }

		}
		catch {
			"The web deployment failed. Please review the parameters you are passing and ensure that they match those expected by the parameters.xml file in your web deploy package .zip file."
			throw
		}
		
		Write-BuildSummaryMessage -name "Deploy" -header "Deployments" -message "WebDeploy: $Package -> $WebURL ($computerName)"
	}
}
tools\PowerDelivery\Register-DeliveryModuleHook.ps1
<#
.Synopsis
Registers a hook function that will be called before or after 
a function in a delivery build script.

.Description
Use this function in a powerdelivery delivery module to register a function 
so that it gets called before or after a function in a delivery build script. 
For example, you can write code in a module that runs before the "Compile" function 
in the importing script.

.Parameter function
The name of the function to hook prefixed with "Pre" or "Post" to have the 
hook code run before or after the importing script's function respectively.

.Example
Register-DeliveryModuleHook "PreCompile" {
	// Your code here
}
#>
function Register-DeliveryModuleHook {
	[CmdletBinding()]
	param(
		[ValidateSet("PreInit", "PostInit", 
					 "PreCompile", "PostCompile", 
					 "PreSetupEnvironment", "PostSetupEnvironment", 
					 "PreTestEnvironment", "PostTestEnvironment", 
					 "PreDeploy", "PostDeploy", 
					 "PreTestAcceptance", "PostTestAcceptance", 
					 "PreTestUnits", "PostTestUnits", 
					 "PreTestCapacity", "PostTestCapacity")]
		[Parameter(Position=0,Mandatory=1)][string] $function,
		[Parameter(Position=1,Mandatory=1)][scriptblock] $action
	)
	
	$powerdelivery.moduleHooks[$function] += $action
}
tools\PowerDelivery\Remove-Pipeline.ps1
<#
.Synopsis
Removes a powerdelivery build pipeline from a TFS project.

.Description
Removes a powerdelivery build pipeline from a TFS project. You will need to use source control history to 
recover your files if you accidentally remove a build pipeline using this cmdlet. The TFS security groups, 
TFS builds, PowerShell script, and YML configuration files associated with the pipeline will be removed.

.Example
Remove-Pipeline -name "MyApp" -collection "http://your-tfsserver/tfs" -project "My Project"

.Parameter name
The name of the product or component that will be no longer be delivered by this pipeline.

.Parameter collection
The URI of the TFS collection to remove powerdelivery from.

.Parameter project
The TFS project to remove powerdelivery from.

.Parameter vsVersion
The version of TFS. Defaults to "10.0" (TFS 2010)
#>
function Remove-Pipeline {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=1)][string] $name,
        [Parameter(Mandatory=1)][string] $collection,
        [Parameter(Mandatory=1)][string] $project,
        [Parameter(Mandatory=0)][string] $vsVersion = "10.0"
    )
	
	$originalDir = Get-Location
	
	$moduleDir = $PSScriptRoot	
	
	$curDir = [System.IO.Path]::GetFullPath($moduleDir)
	$buildsDir = Join-Path -Path $curDir -ChildPath "Pipelines"
	
	try {
	    Write-Host
	    "Remove Pipeline Utility"
	    Write-Host
	    "powerdelivery - http://github.com/eavonius/powerdelivery"
	    Write-Host

	    if ($(get-host).version.major -lt 3) {
	        "Powershell 3.0 or greater is required."
	        exit
	    }

		LoadTFS -vsVersion $vsVersion

	    $outBaseDir = Join-Path -Path $buildsDir -ChildPath $project

        Remove-Item -Path $buildsDir -Force -Recurse -ErrorAction SilentlyContinue | Out-Null

	    mkdir -Force $outBaseDir | Out-Null
	    cd $buildsDir

	    "Removing existing workspace at $collection if it exists..."
	    tf workspace /delete "AddPowerDelivery" /collection:"$collection" | Out-Null

	    if ($LASTEXITCODE -ne 0) {
	        Write-Host "NOTE: Error above is normal. This occurs if there wasn't a mapped working folder already."
	    }
		
		"Connecting to TFS server at $collection to delete builds..."

        $projectCollection = [Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory]::GetTeamProjectCollection($collection)
        $buildServer = $projectCollection.GetService([Microsoft.TeamFoundation.Build.Client.IBuildServer])
        $structure = $projectCollection.GetService([Microsoft.TeamFoundation.Server.ICommonStructureService])

		$buildServerVersion = $buildServer.BuildServerVersion
				
		if ($buildServerVersion -eq 'v3') {
			$powerdelivery.tfsVersion = '2010'
		}
		elseif ($buildServerVersion -eq 'v4') {
			$powerdelivery.tfsVersion = '2012'
		}
		else {
			throw "TFS server must be version 2010 or 2012, a different version was detected."
		}

        $projectInfo = $structure.GetProjectFromName($project)
        if (!$projectInfo) {
            Write-Error "Project $project not found in TFS collection $collection"
            exit
        }

		$buildDictionary = @{
            "$name - Local" = "Local";
            "$name - Commit" = "Commit";
            "$name - Test" = "Test";
            "$name - Capacity Test" = "CapacityTest";
            "$name - Production" = "Production";
        }
		
		$buildDictionary.GETENUMERATOR() | % {
            $buildName = $_.Key
            $buildEnv = $_.Value

            $build = $null

            if ($buildEnv -ne 'Local') {
			
				$buildExists = $false
                try {
                    $build = $buildServer.GetBuildDefinition($project, $buildName)
                    "Found build $buildName, checking for existing builds..."
					
					if ($buildServer.QueryBuilds($build).Length -gt 0) {
						$buildExists = $true
						throw "At least one build exists for $($buildName). You must delete these builds before the pipeline can be fully removed"
					}
                }
                catch {
					if ($buildExists) {
						throw
					}
				}
			}
		}
		
		$buildDictionary.GETENUMERATOR() | % {
            $buildName = $_.Key
            $buildEnv = $_.Value

            $build = $null

            if ($buildEnv -ne 'Local') {
                try {
                    $build = $buildServer.GetBuildDefinition($project, $buildName)
                    "Deleting build $($buildName)..."
					
					$buildServer.DeleteBuildDefinitions(
						@(New-Object -TypeName System.Uri -ArgumentList $build.Uri)
					);
                }
                catch {}
			}
		}

        "Creating TFS workspace for $collection..."
        tf workspace /new /noprompt "AddPowerDelivery" /collection:"$collection"

        "Getting files from project $project..."
        tf get "$project\*" /recursive /noprompt

		$scriptFileName = "$($name).ps1"
		if (Test-Path (Join-Path $outBaseDir $scriptFileName) -PathType Leaf) {
			tf delete "$project\$scriptFileName" /noprompt /recursive | Out-Null
		}
		
		$sharedConfigFileName = "$($name)Shared.yml"
		if (Test-Path (Join-Path $outBaseDir $sharedConfigFileName) -PathType Leaf) {
			tf delete "$project\$sharedConfigFileName" /noprompt /recursive | Out-Null
		}

		$buildDictionary.Values | % {
			$envName = $_
			$configFileName = "$($name)$($envName).yml"
			
			if (Test-Path (Join-Path $outBaseDir $configFileName) -PathType Leaf) {
				tf delete "$project\$configFileName" /noprompt /recursive | Out-Null
			}
		}
		
		"Checking in files removed from source control..."
		tf checkin "$project\*.*" /noprompt /recursive | Out-Null
		
		$groupSecurity = $projectCollection.GetService([Microsoft.TeamFoundation.Server.IGroupSecurityService])
        $appGroups = $groupSecurity.ListApplicationGroups($projectInfo.Uri)

        $buildDictionary.Values | % {
            $envName = $_
            if ($envName -ne 'Commit' -and $envName -ne 'Local') {
                $groupName = "$name $envName Builders"
                $appGroups | % {
                    if ($_.AccountName -eq $groupName) {
                        "Deleting TFS security group $groupName..."
                    	$groupSecurity.DeleteApplicationGroup($_.Sid) | Out-Null
                    }
                }
            }
        }
		
		Write-Host "Delivery pipeline '$name' removed from $collection for project '$project' successfully." -ForegroundColor Green
	}
	finally {
		try {
        	tf workspace /delete "AddPowerDelivery" /collection:"$collection" | Out-Null
		}
		catch {}
        del -Path $buildsDir -Force -Recurse -ErrorAction SilentlyContinue | Out-Null
		cd $originalDir
    }	
}
tools\PowerDelivery\Set-AppPoolIdentity.ps1
<#
.Synopsis
Sets the identity of an IIS web application pool to a specific user account.

.Description
Sets the identity of an IIS web application pool to a specific user account. 
Use the New-WindowsUserAccount cmdlet to create this user beforehand if 
necessary.

.Parameter appPoolName
string - The name of the application pool to modify.

.Parameter userName
string - The username of the account to use for the identity.

.Parameter password
string - The password of the account to use for the identity.

.Parameter computerName
The computer running the IIS website to be modified.

.Example
Set-AppPoolIdentity -appPoolName MySite `
                    -userName 'DOMAIN\MyUser' `
					-password 's@m3p@ss' `
					-computerName MYCOMPUTER
#>
function Set-AppPoolIdentity {
	[CmdletBinding()]
	param(
		[Parameter(Position=0,Mandatory=1)] $appPoolName,
		[Parameter(Position=1,Mandatory=1)] $userName,
		[Parameter(Position=2,Mandatory=1)] $password,
		[Parameter(Position=3,Mandatory=0)] $computerName
	)

	Set-Location $powerdelivery.deployDir

    $logPrefix = "Set-AppPoolIdentity:"
			
	Invoke-Command $computerName {

		Import-Module WebAdministration

        "$using:logPrefix Setting $using:userName as identity of $using:appPoolName application pool on $using:computerName"

		Set-ItemProperty -Path "IIS:\AppPools\$using:appPoolName" -Name ProcessModel.IdentityType -Value 3
		Set-ItemProperty -Path "IIS:\AppPools\$using:appPoolName" -Name ProcessModel.UserName -Value $using:userName
		Set-ItemProperty -Path "IIS:\AppPools\$using:appPoolName" -Name ProcessModel.Password -Value $using:password
	}
}
tools\PowerDelivery\Set-EnvironmentVariable.ps1
function Set-EnvironmentVariable {
    param(
        [Parameter(Position=0,Mandatory=1)] $computerName,
        [Parameter(Position=1,Mandatory=1)] $name,
        [Parameter(Position=2,Mandatory=1)] $value
    )
    
    $logPrefix = "Set-EnvironmentVariable:"

    $computerNames = $computerName -split "," | % { $_.Trim() }

    foreach ($curComputerName in $computerNames) {

        "$logPrefix setting $name on $computerName to $value"

        $invokeArgs = @{
            "ArgumentList" = @($name, $value);
            "ScriptBlock" = {
                param($varName, $varValue)

                if (-not ("win32.nativemethods" -as [type])) {
                    # import sendmessagetimeout from win32
                    add-type -Namespace Win32 -Name NativeMethods -MemberDefinition @"
        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern IntPtr SendMessageTimeout(
            IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam,
            uint fuFlags, uint uTimeout, out UIntPtr lpdwResult);
"@
                }

                $HWND_BROADCAST = [intptr]0xffff
                $WM_SETTINGCHANGE = 0x1a
                $result = [uintptr]::zero

                [Environment]::SetEnvironmentVariable($varName, $varValue, [EnvironmentVariableTarget]::Machine)

                Invoke-Expression "`$Env:$varName = `"$varValue`""
            
                # notify all windows of environment block change
                [win32.nativemethods]::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE,
                    [uintptr]::Zero, "Environment", 2, 5000, [ref]$result);
            };
            "ErrorAction" = "Stop"
        }

        if (!$curComputerName.StartsWith("localhost")) {
            $invokeArgs.Add("ComputerName", $curComputerName)
        }

        Invoke-Command @invokeArgs

        Write-BuildSummaryMessage -name "Configuration" -header "Configurations" -message "Environment Variable: $name -> $value ($computerName)"
    }
}
tools\PowerDelivery\Start-SqlJobs.ps1
<#
.Synopsis
Starts SQL jobs on a Microsoft SQL database server

.Description
Starts SQL jobs on a Microsoft SQL database server. You can start a set of jobs that matches a wildcard.

.Parameter serverName
The SQL server instance on which the jobs will be started.

.Parameter jobs
The jobs to start. Can be a single job name, or a name with wildcards.

.Parameter noWait
Whether to skip waiting for the job to finish. Defaults to false.

.Example
Start-SqlJobs -serverName localhost -jobs MyJobs*
#>
function Start-SqlJobs {
    [CmdletBinding()]
    param(
        [Parameter(Position=0,Mandatory=1)][string] $serverName, 
        [Parameter(Position=1,Mandatory=1)][string] $jobs,
        [Parameter(Position=2,Mandatory=0)][switch] $noWait
    )

    $logPrefix = "Start-SqlJobs:"
    
    [Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | Out-Null

    Write-Host "$logPrefix Starting SQL jobs with pattern $jobs on $serverName"

    try {
        Import-Snapin SqlServerCmdletSnapin100 
    }
    catch {
        Import-Snapin SqlServerCmdletSnapin110 
    }

    try {
        Import-Snapin SqlServerProviderSnapin100 
    }
    catch {
        Import-Snapin SqlServerProviderSnapin110 
    }

    $dataMartServer = New-Object Microsoft.SqlServer.Management.SMO.Server("$serverName")
    $dataMartJobs = $dataMartServer.jobserver.jobs | where-object {$_.name -like "$jobs"}
    
    foreach ($dataMartJob in $dataMartJobs) {   
        $jobName = $dataMartJob.Name
        $dataMartJob.Start()
        Write-Host "$logPrefix SQL Job '$jobName' started"

        $jobRunning = $true

        do {
            $startResult = invoke-sqlcmd -ServerInstance $serverName -database msdb -query "sp_help_jobactivity @job_id = NULL, @job_name = '$jobName'"

            if ($startResult.run_status -eq 3) {
                throw "$logPrefix SQL Job '$jobName' was canceled"
            }

            if ($startResult.run_status -eq 0) {
                throw "$logPrefix SQL Job '$jobName' failed"
            }

            if ($startResult.run_status -eq 1) {
                $jobRunning = $false
                Write-Host "$logPrefix SQL Job '$jobName' completed successfully."
            }
            elseif ($noWait -eq $false) {
                WriteHost "$logPrefix Waiting for SQL job $jobName to finish..."
                sleep 15
            }
            else {
                Write-Host "$logPrefix SQL Job '$jobName' started, not waiting for it to complete."
                $jobRunning = $false
            }
        }
        while ($jobRunning)
    }

    if ($dataMartJobs -eq $null -or $dataMartJobs.length -eq 0) {
        throw "$logPrefix Unable to find SQL Jobs to start matching pattern '$jobs'."
    }

    Write-BuildSummaryMessage -name "Service" -header "Services" -message "Microsoft SQL Job: $jobs ($serverName)"
}
tools\PowerDelivery\Templates\Blank\Build.ps1
# %BUILD_NAME%.ps1
# 
# The script for %BUILD_NAME%'s continous delivery pipeline.
#
# https://github.com/eavonius/powerdelivery

Pipeline '%BUILD_NAME%' -Version '1.0.0'

# Load settings you need for the entire build
#
Init {

    $script:currentDirectory = Get-Location

    # For example
    #
    # $script:mySetting = Get-BuildSetting -name "MySetting"
    # $script:myOtherSetting = Get-BuildSetting -name "MyOtherSetting"
}

# Compile any projects or solutions using MSBuild or other tools
#
Compile {
}

# Run automated unit tests
#
TestUnits {
}

# Deploy your software assets to the target environment
#
Deploy {
}

# Test modifications to the target environment
#
TestEnvironment {
}

# Run automated acceptance tests
#
TestAcceptance {
}

# Run longer and more intensive capacity tests
#
TestCapacity {
}
tools\PowerDelivery\Templates\Blank\BuildCapacityTest.yml
 
tools\PowerDelivery\Templates\Blank\BuildCommit.yml
 
tools\PowerDelivery\Templates\Blank\BuildLocal.yml
 
tools\PowerDelivery\Templates\Blank\BuildProduction.yml
 
tools\PowerDelivery\Templates\Blank\BuildShared.yml
 
tools\PowerDelivery\Templates\Blank\BuildTest.yml
 
tools\PowerDelivery\Templates\Website with Database\Build.ps1
# %BUILD_NAME%.ps1
# 
# The script for %BUILD_NAME%'s continous delivery pipeline.
#
# https://github.com/eavonius/powerdelivery

Pipeline '%BUILD_NAME%' -Version '1.0.0'

# Load settings you need for the entire build
#
Init {
    $script:MSBuild 		= Get-BuildSetting MSBuild
	$script:WebDeploy 		= Get-BuildSetting WebDeploy
    $script:Roundhouse 		= Get-BuildSetting Roundhouse
	$script:UnitTests 		= Get-BuildSetting UnitTests
	$script:AcceptanceTests = Get-BuildSetting AcceptanceTests
}

# Compile any projects or solutions using MSBuild or other tools
#
Compile {

	Invoke-BuildConfigSection $MSBuild Invoke-MSBuild
}

# Run automated unit tests
#
TestUnits {

	Invoke-BuildConfigSection $UnitTests Invoke-MSTest
}

# Deploy your software assets to the target environment
#
Deploy {

    Invoke-BuildConfigSections $Roundhouse Invoke-Roundhouse
	Invoke-BuildConfigSection $WebDeploy Publish-WebDeploy
}

# Test modifications to the target environment
#
TestEnvironment {
}

# Run automated acceptance tests
#
TestAcceptance {

	Invoke-BuildConfigSection $AcceptanceTests Invoke-MSTest
}

# Run longer and more intensive capacity tests
#
TestCapacity {
}
tools\PowerDelivery\Templates\Website with Database\BuildCapacityTest.yml
 
tools\PowerDelivery\Templates\Website with Database\BuildCommit.yml
 
tools\PowerDelivery\Templates\Website with Database\BuildLocal.yml
 
tools\PowerDelivery\Templates\Website with Database\BuildProduction.yml
 
tools\PowerDelivery\Templates\Website with Database\BuildShared.yml
 
tools\PowerDelivery\Templates\Website with Database\BuildTest.yml
 
tools\PowerDelivery\Uninstall-NServiceBusService.ps1
<#
.Synopsis
Stops and then uninstalls a Windows Service for an NServiceBus version 4+ Enterprise Service Bus host.

.Description
Stops and then uninstalls a Windows Service for an NServiceBus version 4+ Enterprise Service Bus host. 
You should call this cmdlet prior to Install-NServiceBusService otherwise files will be in use.

.Parameter ComputerName
The name of the remote computer to uninstall the service from.

.Parameter Name
The name of the service. This can be used to start/stop the service with the "net" command.

.Parameter Directory
A local path on the computer specified by the ComputerName parameter containing the service.

.Example
Uninstall-NServiceBusService `
	-ComputerName MyComputer `
	-Name MyService `
	-Directory "C:\Share\MyService" 
#>
function Uninstall-NServiceBusService{
	param(
		[Parameter(Position=0,Mandatory=1)]$ComputerName,
		[Parameter(Position=1,Mandatory=1)]$Name,
		[Parameter(Position=2,Mandatory=1)]$Directory
	)
	
	Set-Location $powerdelivery.deployDir

    $logPrefix = "Uninstall-NServiceBusService:"

    $computerNames = $ComputerName -split "," | % { $_.Trim() }

    foreach ($curComputerName in $computerNames) {

	    Invoke-Command -ComputerName $curComputerName {

		    if ((Get-Service -Name $using:Name -ErrorAction SilentlyContinue) -ne $null) {

                $priorSvcCmd = (Get-WmiObject -query "SELECT PathName FROM Win32_Service WHERE Name = '$using:Name'").PathName
                $priorSvcExePath = ($priorSvcCmd -replace '^\"([^\"]*)\".*$','$1').Trim('"')
                $priorSvcLocalPath = (Split-Path $priorSvcExePath -Parent)

		 	    $uninstallServiceArgs = @(
		 		    "-uninstall",
		 		    "-serviceName",
		 		    $using:Name
			    )

                "$using:logPrefix Uninstalling previous copy of $using:Name service from $using:curComputerName in $priorSvcLocalPath"
                "$using:logPrefix $priorSvcLocalPath\NServiceBus.Host.exe $uninstallServiceArgs"

			    $uninstallResult = Start-Process -WorkingDirectory $priorSvcLocalPath `
			 	    -FilePath "$priorSvcLocalPath\NServiceBus.Host.exe" `
			  	    -ArgumentList $uninstallServiceArgs `
			 	    -ErrorAction SilentlyContinue `
			 	    -Wait `
			 	    -PassThru

			    if ($false -eq ($uninstallResult -is [System.Diagnostics.Process]))
			    {
			 	    throw "Failed to launch NServiceBus.Host.exe on $curComputerName"
			    }

			    $uninstallResult.WaitForExit()
		    }
        }
	}
}
tools\PowerDelivery\Update-AssemblyInfoFiles.ps1
<#
.Synopsis
Updates the AssemblyInfo.cs/AssemblyInfo.vb files to include the build application version.

.Description
Updates the AssemblyInfo.cs/AssemblyInfo.vb files to include the build application version. 
When a delivery pipeline script uses the Pipeline function to declare a version, this function 
will update the version of assemblies to match that version with the changeset appended at the end.

NOTE: You do not need to call this method if using the Invoke-MSBuild cmdlet to compile your code, 
and if all the AssemblyInfo files you want to change are at or below the path to that project. If 
this is the case, Invoke-MSBuild updates them automatically.

.Parameter path
The path to update the version of AssemblyInfo files under.

.Example
Update-AssemblyInfoFiles "MyPath\MyProject"
#>
function Update-AssemblyInfoFiles {
    [CmdletBinding()]
    param([Parameter(Position=0,Mandatory=1)][string] $path)

    $logPrefix = "Update-AssemblyInfoFiles:"

    $buildAppVersion = Get-BuildAppVersion
    $buildAssemblyVersion = Get-BuildAssemblyVersion
    $assemblyVersionPattern = 'AssemblyVersion\("[0-9]+(\.([0-9]+|\*)){1,3}"\)'
    $fileVersionPattern = 'AssemblyFileVersion\("[0-9]+(\.([0-9]+|\*)){1,3}"\)'
    $assemblyVersion = "AssemblyVersion(""$buildAssemblyVersion"")"
    $fileVersion = "AssemblyFileVersion(""$buildAppVersion"")"
    
    Get-ChildItem -r -Path $path -filter AssemblyInfo.cs | % {
        $filename = $_.Directory.ToString() + '\' + $_.Name
        $powerdelivery.assemblyInfoFiles += ,$filename
        Exec -errorMessage "Unable to update file attributes on $filename" { 
            attrib -r "$filename"
        }

        Write-Host "$varPrefix $assemblyVersion, $fileVersion -> $filename" 

        (Get-Content $filename) | % {
            % {$_ -replace $assemblyVersionPattern, $assemblyVersion } |
            % {$_ -replace $fileVersionPattern, $fileVersion }
        } | Set-Content $filename

        Write-BuildSummaryMessage -name "Configuration" -header "Configurations" -message "AssemblyInfo: $assemblyVersion, $fileVersion -> $filename"
    }
}
tools\PowerDelivery\Update-XmlFile.ps1
<#
.Synopsis
Updates data in an XML file.

.Description
Selects nodes in an XML file using an XPath statement and updates attributes or content 
of those nodes.

.Parameter ComputerName
Optional. A comma-separated list of computers to update the XML file on.

.Parameter FileName
A relative path to the file to update.

.Parameter Replacements
hash - A set of named replacements to make. Each hash entry requires an XPath, Attribute, and NewValue.

.Parameter Namespaces
hash - A set of XML namespaces used in the XPath statements being replaced.

.Example
# MyConfigFile.yml

XmlReplacements:
  SomeFile:
    FileName: SomeDir\SomeFile.xml
    Replacements:
      TabularConnection:
        XPath: //mn:somelement/mn:someotherelement[@name='test']
        Attribute: someOtherAttribute
        NewValue: Hello World!
    Namespaces:
      MyNs:
        Prefix: mn
        URI: http://www.mynamespace.com/

# MyScript.ps1
$XmlReplacements = Get-BuildSetting XmlReplacements
Invoke-BuildConfigSections $XmlReplacements Update-XmlFile
#>
function Update-XmlFile {
    [CmdletBinding()]
    param(
        [Parameter(Position=0,Mandatory=0)] $ComputerName,
        [Parameter(Position=1,Mandatory=1)][string] $FileName, 
        [Parameter(Position=2,Mandatory=1)] $Replacements,
        [Parameter(Position=3,Mandatory=0)] $Namespaces,
        [Parameter(Position=4,Mandatory=0)] $DriveLetter = $powerdelivery.deployDriveLetter
    )
	
	$originalLocation = Get-Location

	Set-Location $powerdelivery.deployDir

    $logPrefix = "Update-XmlFile:"

    if (![String]::IsNullOrWhiteSpace($ComputerName)) {

        $computerNames = $ComputerName -split "," | % { $_.Trim() }
	
	    foreach ($computer in $computerNames) {

            if (![System.IO.Path]::IsPathRooted($FileName)) {
                $FileName = Join-Path (Get-ComputerLocalDeployPath $computer $DriveLetter) $FileName
            }

            Invoke-Command -ComputerName $computer {

                Write-Host "$using:logPrefix Replacing values on $using:computer in $using:FileName"

                [xml]$xmlFile = Get-Content $using:FileName

                $ComputerNamespaces = $using:Namespaces

                $ns = new-object Xml.XmlNamespaceManager $xmlFile.NameTable
                if ($ComputerNamespaces -ne $null) {
                    $ComuterNamespaces.Keys | % {
                        $NamespaceEntry = $ComputerNamespaces[$_]
                        $ns.AddNamespace($NamespaceEntry.Prefix, $NamespaceEntry.URI)
                    }
                }

                $ComputerReplacements = $using:Replacements

                $ComputerReplacements.Keys | % {

                    $replacement = $ComputerReplacements[$_]

                    $node = $xmlFile.SelectSingleNode($replacement.XPath, $ns)

                    if ($node -eq $null) {
                        throw "Path $($replacement.XPath) didn't match a node on $using:computer in $using:FileName"
                    }

                    $value = $replacement.NewValue
                    $attr = $replacement.Attribute

                    Invoke-Expression "`$node.$($attr) = `"$value`""
                }

                $xmlFile.Save($using:FileName)
            }
        }
    }
    else {

        if (![System.IO.Path]::IsPathRooted($FileName)) {
            if ($powerdelivery.blockName -eq 'Compile') {
                $FileName = Join-Path $powerdelivery.currentLocation $FileName
				Set-ItemProperty $FileName -Name IsReadOnly -Value $false
            }
            else {
                $FileName = Join-Path $powerdelivery.deployDir $FileName
            }
        }

        Write-Host "$logPrefix Replacing values in $FileName"

        [xml]$xmlFile = Get-Content $FileName

        $ns = new-object Xml.XmlNamespaceManager $xmlFile.NameTable
        if ($Namespaces -ne $null) {
            $Namespaces.Keys | % {
                $NamespaceEntry = $Namespaces[$_]
                $ns.AddNamespace($NamespaceEntry.Prefix, $NamespaceEntry.URI)
            }
        }

        $ComputerReplacements = $Replacements

        $ComputerReplacements.Keys | % {

            $replacement = $ComputerReplacements[$_]

            $node = $xmlFile.SelectSingleNode($replacement.XPath, $ns)

            if ($node -eq $null) {
                throw "Path $($replacement.XPath) didn't match a node in $FileName"
            }

            $value = $replacement.NewValue
            $attr = $replacement.Attribute

            Invoke-Expression "`$node.$($attr) = `"$value`""
        }

        $xmlFile.Save($FileName)
    }
	
	Set-Location $originalLocation
}
tools\PowerDelivery\Wait-ForLeapFrogBI.ps1
function Wait-ForLeapFrogBI {
    param(
        [Parameter(Position=0,Mandatory=1)] $dataMartConnectionString,
        [Parameter(Position=0,Mandatory=1)] $timeoutMinutes
    )

	Set-Location $powerdelivery.deployDir

    $logPrefix = "Wait-ForLeapFrogBI:"

    # Poll for completion of LeapFrog processing with a timeout
	#
	$timedOut = $true
	$timeout = New-TimeSpan -Minutes $timeoutMinutes
	$stopWatch = [Diagnostics.StopWatch]::StartNew()
	
	"$logPrefix Polling Precedence table to wait $timeoutMinutes minutes for LeapFrogBI packages to process..."
	
	while ($stopWatch.Elapsed -lt $timeout){
	    
		$sqlConnection = New-Object System.Data.OleDb.OleDbConnection
		$sqlConnection.ConnectionString = $dataMartConnectionString
		
		$sqlConnection.Open()
		
		$precedenceRowsExistCmd = New-Object System.Data.OleDb.OleDbCommand("SELECT * FROM [dbo].[Precedence]", $sqlConnection)
		$precedenceRowsExistReader = $precedenceRowsExistCmd.ExecuteReader()
		
		if ($precedenceRowsExistReader.HasRows) {
		
			$failedPackageCmd = New-Object System.Data.OleDb.OleDbCommand("SELECT * FROM [dbo].[Precedence] WHERE Disable <> 1 AND JobStatus = -1 AND JobTryCount = 4", $sqlConnection)
			$failedPackageReader = $failedPackageCmd.ExecuteReader()
			
			if ($failedPackageReader.HasRows) {
				$sqlConnection.Close()
				throw "At least one LeapFrogBI package failed processing."
			}
			else {
				$runningPackageCmd = New-Object System.Data.OleDb.OleDbCommand("SELECT * FROM [dbo].[Precedence] WHERE Disable <> 1 AND JobStatus <> 3", $sqlConnection)
				$runningPackageReader = $runningPackageCmd.ExecuteReader()
			
				if ($runningPackageReader.HasRows -eq $false) {
					"$logPrefix LeapFrogBI processing is complete."
					$sqlConnection.Close()
					$timedOut = $false
					break
				}
				else {
					"$logPrefix LeapFrogBI packages are still running, checking again in 30 seconds..."
				}
			}
		}
		
		$sqlConnection.Close()
	
	    Start-Sleep -Seconds 30
	}
	
	if ($timedOut) {
		throw "Timed out polling for LeapFrogBI packages to complete. Increase the value of the timeoutMinutes parameter."
	}

    Write-BuildSummaryMessage -name "Deploy" -header "Deployments" -message "LeapFrog BI: $dataMartConnectionString"
}
tools\PowerDelivery\Write-BuildSummaryMessage.ps1
<#
.Synopsis
Writes a message to a section in the TFS build summary page.

.Description
Writes a message to a section in the TFS build summary page. Specify as the "name" parameter 
the name of a block from your delivery script. For example, pass "Compile" to have your message 
appear under that section of the build summary.

NOTE: This function will output a warning and not do anything on TFS versions prior to 2012.

.Parameter name
The name of the section to write the message to.

.Parameter header
The text of the section to display (ignored if any other message was already written to this section).

.Paramater message
The message to add to the section specified.

.Example
Write-BuildSummaryMessage "Compile" "Compilations" "My Message"
#>
function Write-BuildSummaryMessage {
    [CmdletBinding()]
    param(
        [Parameter(Position=0,Mandatory=1)][string] $name, 
        [Parameter(Position=1,Mandatory=1)][string] $header, 
        [Parameter(Position=2,Mandatory=1)][string] $message
    )

    $buildServerVersion = $powerdelivery.buildServer.BuildServerVersion
				
	if ($buildServerVersion -eq 'v3') {
        Write-Debug "WARNING: Write-BuildSummaryMessage does nothing on TFS 2010. Upgrade to 2012 to get summary messages (detected TFS $buildServerVersion)."
	}
	elseif ($buildServerVersion -eq 'v4') {
        $buildDetail = Get-CurrentBuildDetail

        $buildSummaryMessage = [Microsoft.TeamFoundation.Build.Client.InformationNodeConverters]::AddCustomSummaryInformation(`
            $buildDetail.Information, $message, $name, $header, 0)

        $buildSummaryMessage.Save()

        $buildDetail.Information.Save()
    }
}
tools\PowerYaml\Functions\Casting.ps1
function Add-CastingFunctions($value) {
	Add-Member -InputObject $value -Name ToInt `
           -MemberType ScriptMethod -PassThru -ErrorAction SilentlyContinue -Value `
           { [int] $this } |
		   Add-Member -Name ToLong `
           -MemberType ScriptMethod -PassThru -ErrorAction SilentlyContinue -Value `
           { [long] $this } |
           Add-Member -Name ToDouble `
           -MemberType ScriptMethod -PassThru -ErrorAction SilentlyContinue -Value `
           { [double] $this } |
           Add-Member -Name ToDecimal `
           -MemberType ScriptMethod -PassThru -ErrorAction SilentlyContinue -Value `
           { [decimal] $this } |
           Add-Member -Name ToByte `
           -MemberType ScriptMethod -PassThru -ErrorAction SilentlyContinue -Value `
           { [byte] $this } |
           Add-Member -Name ToBoolean `
           -MemberType ScriptMethod -PassThru -ErrorAction SilentlyContinue -Value `
           { [System.Boolean]::Parse($this) }

	return $value
		   
}
tools\PowerYaml\Functions\Shadow-Copy.ps1
function Shadow-Copy($file, $shadowPath = "$($env:TEMP)\poweryaml\shadow") {

    if (-not (Test-Path $shadowPath ) ) {
        New-Item $shadowPath -ItemType directory | Out-Null
    }

    try {
        Copy-Item $file $shadowPath -Force -ErrorAction SilentlyContinue
    } catch {
        "Attempted to write over locked file, continuing..." | Write-Debug
    }

    $fileName = (Get-Item $file).Name
    "$shadowPath\$fileName"
}
tools\PowerYaml\Functions\Validator-Functions.ps1
function Validate-File([string] $file) {
    $file_exists = Test-Path $file
    if (-not $file_exists) {
        "ERROR: '$file' does not exist" | Write-Error
        return $false 
    }

    $lines_in_file = [System.IO.File]::ReadAllLines($file)
    $line_tab_detected = Detect-Tab $lines_in_file

    if ($line_tab_detected -gt 0) {
        "ERROR in '$file'`nTAB detected on line $line_tab_detected" | Write-Error 
        return $false
    }

    return $true
}

function Detect-Tab($lines) {
    for($i = 0; $i -lt $lines.count; $i++) {
        if ($lines[$i].Contains("`t")) {
            return ($i + 1) 
        }
    }

    return 0
}
tools\PowerYaml\Functions\YamlDotNet-Integration.ps1
function Load-YamlDotNetLibraries([string] $dllPath, $shadowPath = "$($env:TEMP)\poweryaml\shadow") {
	gci $dllPath | % {
        $shadow = Shadow-Copy -File $_.FullName -ShadowPath $shadowPath
        [Reflection.Assembly]::LoadFrom($shadow)
    } | Out-Null
}

function Get-YamlStream([string] $file) {
    $streamReader = [System.IO.File]::OpenText($file)
    $yamlStream = New-Object YamlDotNet.RepresentationModel.YamlStream

    $yamlStream.Load([System.IO.TextReader] $streamReader)
    $streamReader.Close()
    return $yamlStream
}

function Get-YamlDocument([string] $file) {
    $yamlStream = Get-YamlStream $file
    $document = $yamlStream.Documents[0]
    return $document
}

function Get-YamlDocumentFromString([string] $yamlString) {
    $stringReader = new-object System.IO.StringReader($yamlString)
    $yamlStream = New-Object YamlDotNet.RepresentationModel.YamlStream
    $yamlStream.Load([System.IO.TextReader] $stringReader)

    $document = $yamlStream.Documents[0]
    return $document
}

function Explode-Node($node) {
    if ($node.GetType().Name -eq "YamlScalarNode") {
        return Convert-YamlScalarNodeToValue $node 
    } elseif ($node.GetType().Name -eq "YamlMappingNode") {
        return Convert-YamlMappingNodeToHash $node
    } elseif ($node.GetType().Name -eq "YamlSequenceNode") {
        return Convert-YamlSequenceNodeToList $node
    }
}

function Convert-YamlScalarNodeToValue($node) {
    return Add-CastingFunctions($node.Value)
}

function Convert-YamlMappingNodeToHash($node) {
    $hash = @{}
    $yamlNodes = $node.Children

    foreach($key in $yamlNodes.Keys) {
        $hash[$key.Value] = Explode-Node $yamlNodes[$key]
    }

    return $hash
}

function Convert-YamlSequenceNodeToList($node) {
    $list = @()
    $yamlNodes = $node.Children

    foreach($yamlNode in $yamlNodes) {
        $list += Explode-Node $yamlNode
    }

    return $list
}

tools\PowerYaml\Libs\YamlDotNet.Configuration.dll
 
tools\PowerYaml\Libs\YamlDotNet.Converters.dll
 
tools\PowerYaml\Libs\YamlDotNet.Core.dll
 
tools\PowerYaml\Libs\YamlDotNet.RepresentationModel.dll
 
tools\PowerYaml\LICENSE
 
tools\PowerYaml\PowerYaml.psm1
. $PSScriptRoot\Functions\Casting.ps1
. $PSScriptRoot\Functions\Shadow-Copy.ps1
. $PSScriptRoot\Functions\YamlDotNet-Integration.ps1
. $PSScriptRoot\Functions\Validator-Functions.ps1

<# 
 .Synopsis
  Returns an object that can be dot navigated

 .Parameter FromFile
  File reference to a yaml document

 .Parameter FromString
  Yaml string to be converted
#>
function Get-Yaml([string] $FromString = "", [string] $FromFile = "") {
    if ($FromString -ne "") {
        $yaml = Get-YamlDocumentFromString $FromString
    } elseif ($FromFile -ne "") {
        if ((Validate-File $FromFile)) {
            $yaml = Get-YamlDocument -file $FromFile
        }
    }

    return Explode-Node $yaml.RootNode
}

Load-YamlDotNetLibraries (Join-Path $PSScriptRoot -ChildPath "Libs")
Export-ModuleMember -Function Get-Yaml 
tools\RemoteSharesDeliveryModule\RemoteSharesDeliveryModule.psd1
 
tools\RemoteSharesDeliveryModule\RemoteSharesDeliveryModule.psm1
function Initialize-RemoteSharesDeliveryModule {

	Register-DeliveryModuleHook 'PreSetupEnvironment' {
	
		$moduleConfig = Get-BuildConfig
		$remoteShares = $moduleConfig.RemoteShares

		if ($remoteShares) {
			$remoteShares.Keys | % {
				$invokeArgs = @{}
				
				$remoteShare = $remoteShares[$_]
				
				if ($remoteShare.ComputerName) {
					$invokeArgs.Add('computerName', $remoteShare.ComputerName)
				}
                if ($remoteShare.ShareName) {
					$invokeArgs.Add('shareName', $remoteShare.ShareName)
				}
                if ($remoteShare.ShareDirectory) {
					$invokeArgs.Add('shareDirectory', $remoteShare.ShareDirectory)
				}
                if ($remoteShare.BuildAccountName) {
					$invokeArgs.Add('buildAccountName', $remoteShare.BuildAccountName)
				}
				& New-RemoteShare @invokeArgs
			}
		}
	}
}
tools\RoundhouseDeliveryModule\RoundhouseDeliveryModule.psd1
 
tools\RoundhouseDeliveryModule\RoundhouseDeliveryModule.psm1
function Initialize-RoundhouseDeliveryModule {

	Register-DeliveryModuleHook 'PostDeploy' {
	
		$moduleConfig = Get-BuildConfig
		$roundhouseDatabases = $moduleConfig.Roundhouse

		if ($roundhouseDatabases) {
		
			Invoke-BuildConfigSections $roundhouseDatabases "Invoke-Roundhouse"
		}
	}
}
tools\SSISPackagesDeliveryModule\SSISPackagesDeliveryModule.psd1
 
tools\SSISPackagesDeliveryModule\SSISPackagesDeliveryModule.psm1
function Initialize-SSISPackagesDeliveryModule {

	Register-DeliveryModuleHook 'PostDeploy' {
	
		$moduleConfig = Get-BuildConfig
		$ssisPackages = $moduleConfig.SSISPackages

		if ($ssisPackages) {
		
			Invoke-BuildConfigSections $ssisPackages "Invoke-SSIS"
		}
	}
}
tools\WebDeployDeliveryModule\WebDeployDeliveryModule.psd1
 
tools\WebDeployDeliveryModule\WebDeployDeliveryModule.psm1
function Initialize-WebDeployDeliveryModule {

	Register-DeliveryModuleHook 'PreDeploy' {
	
		$moduleConfig = Get-BuildConfig
		$webDeployments = $moduleConfig.WebDeploy

		if ($webDeployments) {
		
			Invoke-BuildConfigSections $webDeployments "Publish-WebDeploy"
		}
	}
}

Log in or click on link to see number of positives.

In cases where actual malware is found, the packages are subject to removal. Software sometimes has false positives. Moderators do not necessarily validate the safety of the underlying software, only that a package retrieves software from the official distribution point and/or validate embedded software against official distribution point (where distribution rights allow redistribution).

Chocolatey Pro provides runtime protection from possible malware.

Add to Builder Version Downloads Last Updated Status
PowerDelivery 2.2.78 499 Wednesday, September 24, 2014 Unknown
PowerDelivery 2.2.77 376 Tuesday, September 23, 2014 Unknown
PowerDelivery 2.2.76 348 Tuesday, September 23, 2014 Unknown
PowerDelivery 2.2.75 346 Tuesday, September 23, 2014 Unknown
PowerDelivery 2.2.74 374 Thursday, September 11, 2014 Unknown
PowerDelivery 2.2.73 389 Thursday, September 11, 2014 Unknown
PowerDelivery 2.2.72 343 Tuesday, September 9, 2014 Unknown
PowerDelivery 2.2.71 341 Tuesday, September 9, 2014 Unknown
PowerDelivery 2.2.70 380 Thursday, September 4, 2014 Unknown
PowerDelivery 2.2.69 414 Thursday, July 24, 2014 Unknown
PowerDelivery 2.2.68 374 Tuesday, July 15, 2014 Unknown
PowerDelivery 2.2.67 365 Monday, July 14, 2014 Unknown
PowerDelivery 2.2.66 360 Friday, June 20, 2014 Unknown
PowerDelivery 2.2.65 365 Friday, June 20, 2014 Unknown
PowerDelivery 2.2.64 396 Friday, June 13, 2014 Unknown
PowerDelivery 2.2.63 383 Friday, June 13, 2014 Unknown
PowerDelivery 2.2.62 373 Friday, June 13, 2014 Unknown
PowerDelivery 2.2.61 380 Wednesday, June 11, 2014 Unknown
PowerDelivery 2.2.60 364 Wednesday, June 11, 2014 Unknown
PowerDelivery 2.2.59 384 Wednesday, June 11, 2014 Unknown
PowerDelivery 2.2.58 408 Wednesday, June 11, 2014 Unknown
PowerDelivery 2.2.57 349 Wednesday, June 11, 2014 Unknown
PowerDelivery 2.2.56 389 Wednesday, June 11, 2014 Unknown
PowerDelivery 2.2.55 332 Monday, June 9, 2014 Unknown
PowerDelivery 2.2.54 375 Monday, June 9, 2014 Unknown
PowerDelivery 2.2.53 380 Monday, June 9, 2014 Unknown
PowerDelivery 2.2.52 413 Monday, June 9, 2014 Unknown
PowerDelivery 2.2.51 431 Monday, April 28, 2014 Unknown
PowerDelivery 2.2.50 341 Monday, April 28, 2014 Unknown
PowerDelivery 2.2.49 381 Tuesday, April 22, 2014 Unknown
PowerDelivery 2.2.48 384 Monday, April 21, 2014 Unknown
PowerDelivery 2.2.47 393 Monday, April 21, 2014 Unknown
PowerDelivery 2.2.46 427 Wednesday, March 5, 2014 Unknown
PowerDelivery 2.2.45 421 Friday, February 28, 2014 Unknown
PowerDelivery 2.2.44 401 Friday, February 7, 2014 Unknown
PowerDelivery 2.2.43 414 Thursday, February 6, 2014 Unknown
PowerDelivery 2.2.42 358 Thursday, February 6, 2014 Unknown
PowerDelivery 2.2.41 448 Thursday, February 6, 2014 Unknown
PowerDelivery 2.2.40 349 Wednesday, February 5, 2014 Unknown
PowerDelivery 2.2.39 397 Wednesday, February 5, 2014 Unknown
PowerDelivery 2.2.38 405 Tuesday, February 4, 2014 Unknown
PowerDelivery 2.2.37 428 Tuesday, February 4, 2014 Unknown
PowerDelivery 2.2.36 400 Friday, January 31, 2014 Unknown
PowerDelivery 2.2.35 460 Monday, January 27, 2014 Unknown
PowerDelivery 2.2.34 423 Thursday, November 21, 2013 Unknown
PowerDelivery 2.2.33 422 Friday, August 30, 2013 Unknown
PowerDelivery 2.2.32 389 Friday, August 30, 2013 Unknown
PowerDelivery 2.2.31 397 Monday, August 5, 2013 Unknown
PowerDelivery 2.2.30 405 Thursday, August 1, 2013 Unknown
PowerDelivery 2.2.29 430 Thursday, August 1, 2013 Unknown
PowerDelivery 2.2.28 431 Friday, July 26, 2013 Unknown
PowerDelivery 2.2.27 435 Tuesday, July 23, 2013 Unknown
PowerDelivery 2.2.26 403 Tuesday, July 23, 2013 Unknown
PowerDelivery 2.2.25 385 Tuesday, July 23, 2013 Unknown
PowerDelivery 2.2.24 425 Monday, July 22, 2013 Unknown
PowerDelivery 2.2.23 398 Monday, July 22, 2013 Unknown
PowerDelivery 2.2.22 380 Sunday, July 21, 2013 Unknown
PowerDelivery 2.2.21 379 Saturday, July 20, 2013 Unknown
PowerDelivery 2.2.20 433 Saturday, July 20, 2013 Unknown
PowerDelivery 2.2.19 353 Saturday, July 20, 2013 Unknown
PowerDelivery 2.2.18 415 Saturday, July 20, 2013 Unknown
PowerDelivery 2.2.17 419 Saturday, July 20, 2013 Unknown
PowerDelivery 2.2.16 402 Saturday, July 20, 2013 Unknown
PowerDelivery 2.2.15 381 Wednesday, July 17, 2013 Unknown
PowerDelivery 2.2.14 416 Wednesday, July 17, 2013 Unknown
PowerDelivery 2.2.13 405 Tuesday, July 9, 2013 Unknown
PowerDelivery 2.2.12 396 Monday, July 8, 2013 Unknown
PowerDelivery 2.2.11 446 Friday, July 5, 2013 Unknown
PowerDelivery 2.2.10 437 Tuesday, July 2, 2013 Unknown
PowerDelivery 2.2.9 410 Wednesday, June 26, 2013 Unknown
PowerDelivery 2.2.8 361 Wednesday, June 26, 2013 Unknown
PowerDelivery 2.2.7 426 Sunday, June 23, 2013 Unknown
PowerDelivery 2.2.6 441 Friday, June 21, 2013 Unknown
PowerDelivery 2.2.5 401 Friday, June 21, 2013 Unknown
PowerDelivery 2.2.4 409 Friday, June 21, 2013 Unknown
PowerDelivery 2.2.3 393 Thursday, June 20, 2013 Unknown
PowerDelivery 2.2.2 389 Thursday, June 20, 2013 Unknown
PowerDelivery 2.2.1 389 Tuesday, June 18, 2013 Unknown
PowerDelivery 2.2.0 380 Thursday, June 6, 2013 Unknown
PowerDelivery 2.1.6 437 Friday, May 31, 2013 Unknown
PowerDelivery 2.1.3 425 Wednesday, April 24, 2013 Unknown
PowerDelivery 2.1.2 423 Wednesday, April 24, 2013 Unknown
PowerDelivery 2.1.1 413 Wednesday, April 24, 2013 Unknown
PowerDelivery 2.1.0 431 Wednesday, April 24, 2013 Unknown
PowerDelivery 2.0.9 445 Monday, March 25, 2013 Unknown
PowerDelivery 2.0.8 388 Friday, March 15, 2013 Unknown
PowerDelivery 2.0.7 403 Thursday, March 14, 2013 Unknown
PowerDelivery 2.0.6 400 Thursday, March 14, 2013 Unknown
PowerDelivery 2.0.5 393 Thursday, March 14, 2013 Unknown
PowerDelivery 1.0.2 381 Wednesday, February 5, 2014 Unknown

This package has no dependencies.

Discussion for the PowerDelivery Package

Ground Rules:

  • This discussion is only about PowerDelivery and the PowerDelivery package. If you have feedback for Chocolatey, please contact the Google Group.
  • This discussion will carry over multiple versions. If you have a comment about a particular version, please note that in your comments.
  • The maintainers of this Chocolatey Package will be notified about new comments that are posted to this Disqus thread, however, it is NOT a guarantee that you will get a response. If you do not hear back from the maintainers after posting a message below, please follow up by using the link on the left side of this page or follow this link to contact maintainers. If you still hear nothing back, please follow the package triage process.
  • Tell us what you love about the package or PowerDelivery, or tell us what needs improvement.
  • Share your experiences with the package, or extra configuration or gotchas that you've found.
  • If you use a url, the comment will be flagged for moderation until you've been whitelisted. Disqus moderated comments are approved on a weekly schedule if not sooner. It could take between 1-5 days for your comment to show up.
comments powered by Disqus