Downloads:
41,317
Downloads of v 2.2.79:
1,824
Last Update:
21 Nov 2014
Package Maintainer(s):
Software Author(s):
- Jayme Edwards
Tags:
admin- Software Specific:
- Software Site
- Package Specific:
- Package outdated?
- Package broken?
- Contact Maintainers
- Contact Site Admins
- Software Vendor?
- Report Abuse
- Download
PowerDelivery
- 1
- 2
- 3
2.2.79 | Updated: 21 Nov 2014
- Software Specific:
- Software Site
- Package Specific:
- Package outdated?
- Package broken?
- Contact Maintainers
- Contact Site Admins
- Software Vendor?
- Report Abuse
- Download
Downloads:
41,317
Downloads of v 2.2.79:
1,824
Maintainer(s):
Software Author(s):
- Jayme Edwards
Tags:
adminPowerDelivery 2.2.79
Legal Disclaimer: Neither this package nor Chocolatey Software, Inc. are affiliated with or endorsed by Jayme Edwards. The inclusion of Jayme Edwards trademark(s), if any, upon this webpage is solely to identify Jayme Edwards goods or services and not for commercial purposes.
- 1
- 2
- 3
Some Checks Have Failed or Are Not Yet Complete
Not All Tests Have Passed
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:
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
Option 1: Cached Package (Unreliable, Requires Internet - Same As Community)-
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
-
Open Source
-
Download the package:
Download - Follow manual internalization instructions
-
-
Package Internalizer (C4B)
-
Run: (additional options)
choco download powerdelivery --internalize --source=https://community.chocolatey.org/api/v2/
-
For package and dependencies run:
choco push --source="'INTERNAL REPO URL'"
- Automate package internalization
-
Run: (additional options)
3. Copy Your Script
choco upgrade powerdelivery -y --source="'INTERNAL REPO URL'" [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'"
$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.79'
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.79'
end
See docs at https://docs.chef.io/resource_chocolatey_package.html.
cChocoPackageInstaller powerdelivery
{
Name = "powerdelivery"
Version = "2.2.79"
Source = "INTERNAL REPO URL"
}
Requires cChoco DSC Resource. See docs at https://github.com/chocolatey/cChoco.
package { 'powerdelivery':
ensure => '2.2.79',
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.
This package was approved by moderator ferventcoder on 27 Nov 2014.
A deployment framework helping you to continuously deliver software to your customers using Microsoft Team Foundation Server and Windows PowerShell.
<#
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
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
}
}
}
}#>
}
param($installPath, $toolsPath, $package)
$powerdeliveryModule = Join-Path $toolsPath PowerDelivery.psm1
import-module $powerdeliveryModule
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
}
}
}
}
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'
}
}
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)
}
}
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
}
}
<#
.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
}
}
<#
.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
}
}
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
}
}
}
<#
.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."
}
}
}
}
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"
}
}
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."
}
}
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)"
}
}
<#
.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."
}
}
<#
.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."
}
}
<#
.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
}
}
}
}
<#
.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)
}
}
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"
}
function Get-ArrayFromStringOrHash {
param(
[Parameter(Position=0,Mandatory=1)] $computerNames
)
if ($computerNames.GetType().Name -eq 'Hashtable') {
return $computerNames.Values
}
else {
return @($computerNames)
}
}
<#
.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;
}
<#
.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;
}
<#
.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
}
<#
.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
}
<#
.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
}
<#
.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
}
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]
}
}
<#
.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
}
<#
.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
}
<#
.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
}
<#
.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
}
<#
.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
}
<#
.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
}
<#
.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]
}
<#
.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
}
<#
.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
}
<#
.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
}
<#
.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):"
}
<#
.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]
}
<#
.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."
}
}
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."
}
}
}
<#
.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."
}
}
}
<#
.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"
}
<#
.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
}
}
<#
.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)"
}
}
}
<#
.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"
}
}
}
}
<#
.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
}
<#
.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`""
}
}
<#
.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 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=4,Mandatory=0)][string] $packageArgs
)
Set-Location $powerdelivery.deployDir
$logPrefix = "Invoke-SSISPackage:"
$computerNames = $computerName -split "," | % { $_.Trim() }
$logFileName = [System.IO.Path]::GetFileNameWithoutExtension("$package") + ".log"
$dropLogPath = [System.IO.Path]::GetDirectoryName("$package")
foreach ($curComputerName in $computerNames) {
$remoteTempPath = New-RemoteTempPath $curComputerName $package
$invokeArgs = @{
"ComputerName" = $curComputerName;
"ArgumentList" = @($logPrefix, $package, $dtExecPath, $packageArgs, $dropLogPath, $remoteTempPath, $logFileName);
"ScriptBlock" = {
param($logPrefix, $package, $dtExecPath, $packageArgs, $dropLogPath, $remoteTempPath, $logFileName)
$remoteLogFile = Join-Path "$remoteTempPath" "$logFileName"
# 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 directory it was executed from.
#
if (Test-Path $remoteLogFile) {
if ([System.IO.Path]::GetDirectoryName("$remoteLogFile") -ne $dropLogPath) {
if (!(Test-Path "$dropLogPath")) {
New-Item -ItemType Directory -Path $dropLogPath | Out-Null
}
Write-Host "$varLogPrefix $remoteLogFile -> $dropLogPath"
Copy-Item "$remoteLogFile" "$dropLogPath"
}
}
};
"ErrorAction" = "Stop"
}
Invoke-Command @invokeArgs
Write-BuildSummaryMessage -name "Deploy" -header "Deployments" -message "SSIS: $package ($curComputerName)"
}
}
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
}
<#
.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."
}
}
}
}
<#
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) }
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."
}
}
}
<#
.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
}
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"
}
}
<#
.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."
}
}
}
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)"
}
}
<#
.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
}
<#
.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
}
}
<#
.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
}
}
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)"
}
}
<#
.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)"
}
# %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 {
}
# %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 {
}
<#
.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()
}
}
}
}
<#
.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
}
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"
}
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
}
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"
}
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
}
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
}
. $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
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
}
}
}
}
function Initialize-RoundhouseDeliveryModule {
Register-DeliveryModuleHook 'PostDeploy' {
$moduleConfig = Get-BuildConfig
$roundhouseDatabases = $moduleConfig.Roundhouse
if ($roundhouseDatabases) {
Invoke-BuildConfigSections $roundhouseDatabases "Invoke-Roundhouse"
}
}
}
function Initialize-SSISPackagesDeliveryModule {
Register-DeliveryModuleHook 'PostDeploy' {
$moduleConfig = Get-BuildConfig
$ssisPackages = $moduleConfig.SSISPackages
if ($ssisPackages) {
Invoke-BuildConfigSections $ssisPackages "Invoke-SSIS"
}
}
}
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.
- PowerDelivery.2.2.79.nupkg (ae53c303f74a) - ## / 56
- YamlDotNet.Configuration.dll (fd842329e31c) - ## / 54
- YamlDotNet.Converters.dll (23dd69b6043d) - ## / 57
- YamlDotNet.Core.dll (bbb2783aa136) - ## / 57
- YamlDotNet.RepresentationModel.dll (fb6bd421ca71) - ## / 57
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.79 | 1824 | Friday, November 21, 2014 | Approved | |
PowerDelivery 2.2.78 | 517 | Wednesday, September 24, 2014 | Unknown | |
PowerDelivery 2.2.77 | 405 | Tuesday, September 23, 2014 | Unknown | |
PowerDelivery 2.2.76 | 371 | Tuesday, September 23, 2014 | Unknown | |
PowerDelivery 2.2.75 | 368 | Tuesday, September 23, 2014 | Unknown | |
PowerDelivery 2.2.74 | 401 | Thursday, September 11, 2014 | Unknown | |
PowerDelivery 2.2.73 | 410 | Thursday, September 11, 2014 | Unknown | |
PowerDelivery 2.2.72 | 368 | Tuesday, September 9, 2014 | Unknown | |
PowerDelivery 2.2.71 | 368 | Tuesday, September 9, 2014 | Unknown | |
PowerDelivery 2.2.70 | 398 | Thursday, September 4, 2014 | Unknown | |
PowerDelivery 2.2.69 | 432 | Thursday, July 24, 2014 | Unknown | |
PowerDelivery 2.2.68 | 386 | Tuesday, July 15, 2014 | Unknown | |
PowerDelivery 2.2.67 | 386 | Monday, July 14, 2014 | Unknown | |
PowerDelivery 2.2.66 | 377 | Friday, June 20, 2014 | Unknown | |
PowerDelivery 2.2.65 | 391 | Friday, June 20, 2014 | Unknown | |
PowerDelivery 2.2.64 | 410 | Friday, June 13, 2014 | Unknown | |
PowerDelivery 2.2.63 | 401 | Friday, June 13, 2014 | Unknown | |
PowerDelivery 2.2.62 | 399 | Friday, June 13, 2014 | Unknown | |
PowerDelivery 2.2.61 | 402 | Wednesday, June 11, 2014 | Unknown | |
PowerDelivery 2.2.60 | 384 | Wednesday, June 11, 2014 | Unknown | |
PowerDelivery 2.2.59 | 401 | Wednesday, June 11, 2014 | Unknown | |
PowerDelivery 2.2.58 | 428 | Wednesday, June 11, 2014 | Unknown | |
PowerDelivery 2.2.57 | 379 | Wednesday, June 11, 2014 | Unknown | |
PowerDelivery 2.2.56 | 410 | Wednesday, June 11, 2014 | Unknown | |
PowerDelivery 2.2.55 | 348 | Monday, June 9, 2014 | Unknown | |
PowerDelivery 2.2.54 | 393 | Monday, June 9, 2014 | Unknown | |
PowerDelivery 2.2.53 | 403 | Monday, June 9, 2014 | Unknown | |
PowerDelivery 2.2.52 | 433 | Monday, June 9, 2014 | Unknown | |
PowerDelivery 2.2.51 | 447 | Monday, April 28, 2014 | Unknown | |
PowerDelivery 2.2.50 | 361 | Monday, April 28, 2014 | Unknown | |
PowerDelivery 2.2.49 | 400 | Tuesday, April 22, 2014 | Unknown | |
PowerDelivery 2.2.48 | 402 | Monday, April 21, 2014 | Unknown | |
PowerDelivery 2.2.47 | 411 | Monday, April 21, 2014 | Unknown | |
PowerDelivery 2.2.46 | 446 | Wednesday, March 5, 2014 | Unknown | |
PowerDelivery 2.2.45 | 440 | Friday, February 28, 2014 | Unknown | |
PowerDelivery 2.2.44 | 421 | Friday, February 7, 2014 | Unknown | |
PowerDelivery 2.2.43 | 432 | Thursday, February 6, 2014 | Unknown | |
PowerDelivery 2.2.42 | 383 | Thursday, February 6, 2014 | Unknown | |
PowerDelivery 2.2.41 | 464 | Thursday, February 6, 2014 | Unknown | |
PowerDelivery 2.2.40 | 371 | Wednesday, February 5, 2014 | Unknown | |
PowerDelivery 2.2.39 | 413 | Wednesday, February 5, 2014 | Unknown | |
PowerDelivery 2.2.38 | 418 | Tuesday, February 4, 2014 | Unknown | |
PowerDelivery 2.2.37 | 446 | Tuesday, February 4, 2014 | Unknown | |
PowerDelivery 2.2.36 | 418 | Friday, January 31, 2014 | Unknown | |
PowerDelivery 2.2.35 | 480 | Monday, January 27, 2014 | Unknown | |
PowerDelivery 2.2.34 | 446 | Thursday, November 21, 2013 | Unknown | |
PowerDelivery 2.2.33 | 443 | Friday, August 30, 2013 | Unknown | |
PowerDelivery 2.2.32 | 406 | Friday, August 30, 2013 | Unknown | |
PowerDelivery 2.2.31 | 418 | Monday, August 5, 2013 | Unknown | |
PowerDelivery 2.2.30 | 424 | Thursday, August 1, 2013 | Unknown | |
PowerDelivery 2.2.29 | 451 | Thursday, August 1, 2013 | Unknown | |
PowerDelivery 2.2.28 | 451 | Friday, July 26, 2013 | Unknown | |
PowerDelivery 2.2.27 | 452 | Tuesday, July 23, 2013 | Unknown | |
PowerDelivery 2.2.26 | 426 | Tuesday, July 23, 2013 | Unknown | |
PowerDelivery 2.2.25 | 397 | Tuesday, July 23, 2013 | Unknown | |
PowerDelivery 2.2.24 | 443 | Monday, July 22, 2013 | Unknown | |
PowerDelivery 2.2.23 | 428 | Monday, July 22, 2013 | Unknown | |
PowerDelivery 2.2.22 | 407 | Sunday, July 21, 2013 | Unknown | |
PowerDelivery 2.2.21 | 403 | Saturday, July 20, 2013 | Unknown | |
PowerDelivery 2.2.20 | 451 | Saturday, July 20, 2013 | Unknown | |
PowerDelivery 2.2.19 | 376 | Saturday, July 20, 2013 | Unknown | |
PowerDelivery 2.2.18 | 431 | Saturday, July 20, 2013 | Unknown | |
PowerDelivery 2.2.17 | 444 | Saturday, July 20, 2013 | Unknown | |
PowerDelivery 2.2.16 | 422 | Saturday, July 20, 2013 | Unknown | |
PowerDelivery 2.2.15 | 400 | Wednesday, July 17, 2013 | Unknown | |
PowerDelivery 2.2.14 | 436 | Wednesday, July 17, 2013 | Unknown | |
PowerDelivery 2.2.13 | 423 | Tuesday, July 9, 2013 | Unknown | |
PowerDelivery 2.2.12 | 414 | Monday, July 8, 2013 | Unknown | |
PowerDelivery 2.2.11 | 468 | Friday, July 5, 2013 | Unknown | |
PowerDelivery 2.2.10 | 458 | Tuesday, July 2, 2013 | Unknown | |
PowerDelivery 2.2.9 | 433 | Wednesday, June 26, 2013 | Unknown | |
PowerDelivery 2.2.8 | 376 | Wednesday, June 26, 2013 | Unknown | |
PowerDelivery 2.2.7 | 443 | Sunday, June 23, 2013 | Unknown | |
PowerDelivery 2.2.6 | 461 | Friday, June 21, 2013 | Unknown | |
PowerDelivery 2.2.5 | 419 | Friday, June 21, 2013 | Unknown | |
PowerDelivery 2.2.4 | 427 | Friday, June 21, 2013 | Unknown | |
PowerDelivery 2.2.3 | 410 | Thursday, June 20, 2013 | Unknown | |
PowerDelivery 2.2.2 | 406 | Thursday, June 20, 2013 | Unknown | |
PowerDelivery 2.2.1 | 408 | Tuesday, June 18, 2013 | Unknown | |
PowerDelivery 2.2.0 | 401 | Thursday, June 6, 2013 | Unknown | |
PowerDelivery 2.1.6 | 457 | Friday, May 31, 2013 | Unknown | |
PowerDelivery 2.1.3 | 448 | Wednesday, April 24, 2013 | Unknown | |
PowerDelivery 2.1.2 | 442 | Wednesday, April 24, 2013 | Unknown | |
PowerDelivery 2.1.1 | 432 | Wednesday, April 24, 2013 | Unknown | |
PowerDelivery 2.1.0 | 446 | Wednesday, April 24, 2013 | Unknown | |
PowerDelivery 2.0.9 | 469 | Monday, March 25, 2013 | Unknown | |
PowerDelivery 2.0.8 | 408 | Friday, March 15, 2013 | Unknown | |
PowerDelivery 2.0.7 | 420 | Thursday, March 14, 2013 | Unknown | |
PowerDelivery 2.0.6 | 418 | Thursday, March 14, 2013 | Unknown | |
PowerDelivery 2.0.5 | 410 | Thursday, March 14, 2013 | Unknown | |
PowerDelivery 1.0.2 | 400 | Wednesday, February 5, 2014 | Unknown |
This package has no dependencies.
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.