Public/Invoke-PSDeploy.ps1
Function Invoke-PSDeploy { <# .SYNOPSIS Invoke PSDeploy .DESCRIPTION Invoke PSDeploy Searches for .PSDeploy.ps1 files in the current and nested paths, and invokes their deployment See Get-Help about_PSDeploy for more information. .PARAMETER Path Path to a specific PSDeploy.ps1 file, or to a folder that we recursively search for *.*Deploy.ps1 or *PSDeploy.ps1 files Defaults to the current path .PARAMETER Recurse If path is a folder, whether to recursively search for *.psdeploy.ps1 files under that folder Defaults to $True .PARAMETER Tags Only invoke deployments that are tagged with all of the specified Tags (-and, not -or) .PARAMETER DeploymentRoot Root path used to determing relative paths. Defaults to the Path parameter. .PARAMETER PSDeployTypePath Specify a PSDeploy.yml file that maps DeploymentTypes to their scripts. This defaults to the PSDeploy.yml in the PSDeploy module folder .PARAMETER Force Force deployment, skipping prompts and confirmation .EXAMPLE Invoke-PSDeploy # Run deployments from any file named *.psdeploy.ps1 found under the current folder or any nested folders. # Prompts to confirm .EXAMPLE Invoke-PSDeploy -Path C:\Git\Module1\deployments\mymodule.psdeploy.ps1 -force # Run deployments from mymodule.psdeploy.ps1. # Don't prompt to confirm. .EXAMPLE Invoke-PSDeploy -Path C:\Git\Module1\deployments\mymodule.psdeploy.ps1 -DeploymentRoot C:\Git\Module1 -Tags Prod # Run deployments from mymodule.psdeploy.ps1. # Use C:\Git\Module1 to build any relative paths. # Only run deployments tagged 'Prod' .LINK about_PSDeploy .LINK https://github.com/RamblingCookieMonster/PSDeploy .LINK Deploy .LINK By .LINK To .LINK FromSource .LINK Tagged .LINK WithOptions .LINK WithPreScript .LINK WithPostScript .LINK DependingOn .LINK Get-PSDeployment .LINK Get-PSDeploymentType .LINK Get-PSDeploymentScript #> [cmdletbinding( SupportsShouldProcess = $True, ConfirmImpact='High' )] Param( [validatescript({Test-Path -Path $_ -ErrorAction Stop})] [parameter( ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [string[]]$Path = '.', [string]$DeploymentRoot, # Add later. Pass on to Invoke-PSDeployment. [validatescript({Test-Path -Path $_ -PathType Leaf -ErrorAction Stop})] [string]$PSDeployTypePath = $(Join-Path $ModulePath PSDeploy.yml), [string[]]$Tags, [bool]$Recurse = $True, [switch]$Force ) Begin { # This script reads a deployment YML, deploys files or folders as defined Write-Verbose "Running Invoke-PSDeploy with ParameterSetName '$($PSCmdlet.ParameterSetName)' and params: $($PSBoundParameters | Out-String)" $RejectAll = $false $ConfirmAll = $false $DeploymentFiles = New-Object System.Collections.ArrayList $InvokePSDeploymentParams = @{} if($PSBoundParameters.ContainsKey('Confirm')) { $InvokePSDeploymentParams.add('Confirm',$Confirm) } if($PSBoundParameters.ContainsKey('Force')) { $InvokePSDeploymentParams.add('Force',$Force) } if($PSBoundParameters.ContainsKey('PSDeployTypePath')) { $InvokePSDeploymentParams.add('PSDeployTypePath',$PSDeployTypePath) } } Process { foreach( $PathItem in $Path ) { if( -not $PSBoundParameters.ContainsKey('DeploymentRoot') ) { try { $Item = Get-Item $PathItem -ErrorAction Stop } catch { Write-Error "Could not determine whether path '$PathItem' is a container or file" Throw $_ } if($Item.PSIsContainer) { $DeploymentRoot = $PathItem } else { $DeploymentRoot = $Item.DirectoryName } } # Create a map for deployments Try { # Debating whether to make this a terminating error. # Stop all deployments because one is misconfigured? # I'm going with Copy-Item precedent. # Not terminating, so try catch is superfluous. Feel free to make this strict... [void]$DeploymentFiles.AddRange( @( Resolve-DeployScripts -Path $PathItem -Recurse $Recurse ) ) if ($DeploymentFiles.count -gt 0) { Write-Verbose "Working with $($DeploymentFiles.Count) deployment files:`n$($DeploymentFiles | Out-String)" } else { Write-Warning "No *.PSDeploy.ps1 files found under '$PathItem'" } } Catch { Throw "Error retrieving deployments from '$PathItem':`n$_" } } # Parse $GetPSDeployParams = @{Path = $DeploymentFiles} if($PSBoundParameters.ContainsKey('Tags')) { $GetPSDeployParams.Add('Tags',$Tags) } #Resolve relative paths... Thanks Oisin! http://stackoverflow.com/a/3040982/3067642 if($PSBoundParameters.ContainsKey('DeploymentRoot')) { $DeploymentRoot = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($DeploymentRoot) $GetPSDeployParams.Add('DeploymentRoot', $DeploymentRoot) } $DeploymentScripts = Get-PSDeploymentScript # Handle Dependencies $ToDeploy = Get-PSDeployment @GetPSDeployParams $ReservedOptions = [string[]]'SourceIsAbsolute' foreach($Deployment in $ToDeploy) { $Type = $Deployment.DeploymentType $TheseParams = @{'DeploymentParameters' = @{}} if($Deployment.DeploymentOptions.Keys.Count -gt 0 -and -not ($Type -eq 'Task' -and $Type.Source -is [scriptblock])) { # Shoehorn Deployment Options into DeploymentParameters # Needed if we support both yml and ps1 definitions... # First, get the script, parse out parameters, restrict splatting to valid params $DeploymentScript = $DeploymentScripts.$Type $ValidParameters = Get-ParameterName -Command $DeploymentScript $FilteredOptions = @{} foreach($key in $Deployment.DeploymentOptions.Keys) { if($ReservedOptions -contains $key) { continue } elseif($ValidParameters -contains $key) { $FilteredOptions.Add($key, $Deployment.DeploymentOptions.$key) } else { Write-Warning "WithOption '$Key' is not a valid parameter for '$Type'" } } $hash = @{$Type = $FilteredOptions} $TheseParams.DeploymentParameters = $hash } $Deploy = $True #anti pattern! Best I could come up with to handle both prescript fail and dependencies if($Deployment.Dependencies.ScriptBlock) { Write-Verbose "Checking dependency:`n$($Deployment.Dependencies.ScriptBlock)" if( -not $( . $Deployment.Dependencies.ScriptBlock ) ) { $Deploy = $False Write-Warning "Skipping Deployment '$($Deployment.DeploymentName)', did not pass scriptblock`n$($Deployment.Dependencies.ScriptBlock | Out-String)" } } if($Deployment.PreScript.Count -gt 0) { $ExistingEA = $ErrorActionPreference foreach($script in $Deployment.Prescript) { if($Script.SkipOnError) { Try { Write-Verbose "Invoking pre script: $($Script.ScriptBlock)" $ErrorActionPreference = 'Stop' . $Script.ScriptBlock } Catch { $Deploy = $False Write-Error $_ } } else { . $Script.ScriptBlock } } $ErrorActionPreference = $ExistingEA } if($Deploy) { $Deployment | Invoke-PSDeployment @TheseParams @InvokePSDeploymentParams } if($Deployment.PostScript.Count -gt 0) { foreach($script in $Deployment.PostScript) { Write-Verbose "Invoking post script: $($Script.ScriptBlock)" . $Script.ScriptBlock } } } } } |