BuildMasterAutomation.psm1


Add-Type -AssemblyName 'System.Web'

$functionsDir = Join-Path -Path $PSScriptRoot -ChildPath 'Functions'
if( (Test-Path -Path $functionsDir -PathType Container) )
{
    foreach( $item in (Get-ChildItem -Path $functionsDir -Filter '*.ps1') )
    {
        . $item.FullName
    }
}



function Add-BMObjectParameter
{
    <#
    .SYNOPSIS
    Adds an object to a parameter hashtable based on the object's type.
 
    .DESCRIPTION
    The `Add-BMObjectParameter` adds a parameter to a parameter hashtable based on the parameter's type. Many of BuildMaster's APIs take an ID or a name. For example, many of the Release and Package Deployment methods accept either an `applicationId` parameter *or* an `applicationName` parameter. This function takes either application object, an application ID, or an application name, figures out what was passed, and adds the correct `applicationId`, or `applicationName` parameter.
 
    The hashtable is passed to the `Parameter` parameter (or you can pipe it to `Add-BMObjectParameter` function). Use the `PassThru` switch to return the hashtable to the pipeline.
 
    Pass the name of the parameter, without the `Id` or `Name` suffix via the `Name` parameter, e.g. `pipeline`, `application`, `release`, etc.
 
    Pass the value of the parameter to the `Value` parameter. This can be an object, an integer, or a string. If you pass an integer, a parameter with the name `$($Name)Id` is added to the hashtable. If you pass a string, a parameter with the name `$($Name)Name` is added. If you pass an object, `Add-BMObjectParameter` looks for `id`, `$($Name)_Id`, `name`, or `$($Name)_Name` properties on it (in that order) and adds an `$($Name)Id` parameter if it finds an ID property or a `$($Name)Name` parameter if it finds an Name property.
 
    .EXAMPLE
    $parameters | Add-BMObjectParameter -Name 'application' -Value $app
 
    Demonstrates how to add an ID or Name parameter `$parameters` hashtable. In this case, `Add-BMObjectParameter` will check if `$app` is an integer. If it is, it will add an `applicationId` parameter to the `$parameters` hashtable. If `$app` is a string, it will add an `applicationName` parameter. If `$app` is an object, `Add-BMObjectParameter` will look for an `Application_Id` or `id` property. If it finds one, it adds an `applicationId` parameter to `$parameters`. If it doesn't, it looks for an `Application_Name` or `name` property. If it finds one, it adds an `applicationName` parameter to `$parameters`. Otherwise, it will write an error and not add anything.
 
    .EXAMPLE
    $parameter | Add-BMObjectParmaeter -Name 'application -Value $app -PassThru | Add-BMObjectParameter -Name 'pipeline' -Value $pipeline
 
    Demonstrates how you can use the `PassThru` switch to add multiple parameters to a parameters hashtable.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [hashtable]
        # The hashtable to add the parameter to.
        $Parameter,

        [Parameter(Mandatory=$true)]
        [string]
        # The name of the parameter, *without* the `Id` or `Name` suffix. The suffix is added automatically based on the type of the parameter value.
        $Name,

        [Parameter(Mandatory=$true)]
        [object]
        # The object
        $Value,

        [Switch]
        $PassThru,

        [Switch]
        # The parameters are being used in the native API, which has a different naming convention.
        $ForNativeApi
    )

    process
    {
        Set-StrictMode -Version 'Latest'
        Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

        $idParamName = '{0}Id' -f $Name
        $nameParamName = '{0}Name' -f $Name
        $idPropertyName = '{0}_Id' -f $Name
        $namePropertyName = '{0}_Name' -f $Name

        if( $ForNativeApi )
        {
            $idParamName = $idPropertyName
            $nameParamName = $namePropertyName
        }

        if( $Value -is [int] )
        {
            $Parameter[$idParamName] = $Value
        }
        elseif( $Value -is [string] )
        {
            $Parameter[$nameParamName] = $Value
        }
        elseif( $Value | Get-Member -Name 'id' )
        {
            $Parameter[$idParamName] = $Value.id
        }
        elseif( $Value | Get-Member -Name $idPropertyName )
        {
            $Parameter[$idParamName] = $Value.$idPropertyName
        }
        elseif( $Value | Get-Member -Name 'name' )
        {
            $Parameter[$nameParamName] = $Value.name
        }
        elseif( $Value | Get-Member -Name $namePropertyName )
        {
            $Parameter[$nameParamName] = $value.$namePropertyName
        }

        if( $PassThru )
        {
            return $Parameter
        }
    }


}



# Copyright 2016 - 2018 WebMD Health Services
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

function Add-PSTypeName
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ValueFromPipeline)]
        [object]$InputObject,

        [Parameter(Mandatory,ParameterSetName='Server')]
        [Switch]$Server
    )

    process
    {
        Set-StrictMode -Version 'Latest'

        $typeName = 'Inedo.BuildMaster.{0}' -f $PSCmdlet.ParameterSetName
        $InputObject.pstypenames.Add( $typeName )
        $InputObject
    }
}


function Disable-BMApplication
{
    <#
    .SYNOPSIS
    Disables a BuildMaster application
     
    .DESCRIPTION
    Disables an application in BuildMaster. This doesn't delete the application. Just removes it from the UI.
 
    This function uses the native API, which can change without notice between releases. The API key you use must have access to the native API.
     
    .EXAMPLE
    Disable-BMApplication -Session $session -ID 494
 
    Demonstrates how to delete an application using its ID.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [object]
        # The session to use when connecting to BuildMaster. Use `New-BMSession` to create session objects.
        $Session,

        [Parameter(Mandatory=$true)]
        [int]
        # The name of the application to get.
        $ID
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    Invoke-BMNativeApiMethod -Session $Session -Name 'Applications_DeactivateApplication' -Parameter @{ Application_Id = $ID } -Method Post
}



function Disable-BMEnvironment
{
    <#
    .SYNOPSIS
    Disable an environment in BuildMaster.
 
    .DESCRIPTION
    The `Disable-BMEnvironment` function disables an environment in BuildMaster. Environments are permanent and can only be disabled, never deleted. Pass the name of the environment to disable to the `Name` parameter. If the environment doesn't exist, you'll get an error.
 
    Pass the session to the BuildMaster instance where you want to disable the environment to the `Session` parameter. Use `New-BMSession` to create a session object.
 
    This function uses BuildMaster's infrastructure management API.
 
    .EXAMPLE
    Disable-BMEnvironment -Session $session -Name 'Dev'
 
    Demonstrates how to disable an environment
 
    .EXAMPLE
    Get-BMEnvironment -Session $session -Name 'DevOld' | Disable-BMEnvironment -Session $session
 
    Demonstrates that you can pipe the objects returned by `Get-BMEnvironment` into `Disable-BMEnvironment` to disable those environments.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory)]
        # An object representing the instance of BuildMaster to connect to. Use `New-BMSession` to create session objects.
        [object]$Session,

        [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
        # The name of the environment to disable.
        [string]$Name
    )

    process
    {
        Set-StrictMode -Version 'Latest'
        Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

        $environment = Get-BMEnvironment -Session $Session -Name $Name -Force
        if( $environment -and $environment.active )
        {
            Invoke-BMNativeApiMethod -Session $session -Name 'Environments_DeleteEnvironment' -Parameter @{ 'Environment_Id' = $environment.id } -Method Post
        }
    }
}


function Enable-BMEnvironment
{
    <#
    .SYNOPSIS
    Enable an environment in BuildMaster.
 
    .DESCRIPTION
    The `Enable-BMEnvironment` function enables an environment in BuildMaster. Environments are permanent and can only be disabled, never deleted. Pass the name of the environment to enable to the `Name` parameter. If the environment doesn't exist, you'll get an error.
 
    Pass the session to the BuildMaster instance where you want to enable the environment to the `Session` parameter. Use `New-BMSession` to create a session object.
 
    This function uses BuildMaster's native and infrastructure APIs.
 
    .EXAMPLE
    Enable-BMEnvironment -Session $session -Name 'Dev'
 
    Demonstrates how to enable an environment
 
    .EXAMPLE
    Get-BMEnvironment -Session $session -Name 'DevOld' -Force | Enable-BMEnvironment -Session $session
 
    Demonstrates that you can pipe the objects returned by `Get-BMEnvironment` into `Enable-BMEnvironment` to enable those environments.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory)]
        # An object representing the instance of BuildMaster to connect to. Use `New-BMSession` to create session objects.
        [object]$Session,

        [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
        # The name of the environment to enable.
        [string]$Name
    )

    process
    {
        Set-StrictMode -Version 'Latest'
        Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

        $environment = Get-BMEnvironment -Session $Session -Name $Name -Force
        if( $environment -and -not $environment.active )
        {
            Invoke-BMNativeApiMethod -Session $session -Name 'Environments_UndeleteEnvironment' -Parameter @{ 'Environment_Id' = $environment.id } -Method Post
        }
    }
}


function Get-BMApplication
{
    <#
    .SYNOPSIS
    Gets BuildMaster applications.
     
    .DESCRIPTION
    The `Get-BMApplication` function gets all active applications from an instance of BuildMaster. Use the `Force` switch to include inactive applications. To get a specific application, pass the name to the `Name` parameter. Active and inactive applications are returned. If an application with the name doesn't exist, you'll get nothing back.
     
    Uses the BuildMaster native API, which can change without notice between releases. By default, this function returns *all* applications.
     
    .EXAMPLE
    Get-BMApplication -Session $session
 
    Demonstrates how to get all active BuildMaster applications
 
    .EXAMPLE
    Get-BMApplication -Session $session -Force
 
    Demonstrates how to get all active *and* inactive/disabled BuildMaster applications.
 
    .EXAMPLE
    Get-BMApplication -Session $session -Name 'MyApplication'
 
    Demonstrates how to get a specific application.
    #>

    [CmdletBinding(DefaultParameterSetName='AllApplications')]
    param(
        [Parameter(Mandatory=$true)]
        [object]
        # The session to use when connecting to BuildMaster. Use `New-BMSession` to create session objects.
        $Session,

        [Parameter(ParameterSetName='SpecificApplication',Mandatory=$true)]
        [string]
        # The name of the application to get. By default, all applications are returned.
        $Name,

        [Parameter(ParameterSetName='AllApplications')]
        [Switch]
        # Force `Get-BMApplication` to return inactive/disabled applications.
        $Force
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
    # Invoke-BMNativeApiMethod uses POST, but we're reading data, so always make the request.
    $WhatIfPreference = $false

    $parameters = @{
                        Application_Count = 0;
                        IncludeInactive_Indicator = ($Force.IsPresent -or $PSCmdlet.ParameterSetName -eq 'SpecificApplication');
                   } 

    Invoke-BMNativeApiMethod -Session $Session -Name 'Applications_GetApplications' -Parameter $parameters -Method Post |
        Where-Object { 
            if( $Name )
            {
                return $_.Application_Name -eq $Name
            }
            return $true
        }
}



function Get-BMApplicationGroup
{
    <#
    .SYNOPSIS
    Gets BuildMaster application groups.
     
    .DESCRIPTION
    The `Get-BMApplicationGroup` function gets all application groups from an instance of BuildMaster. To get a specific application group, pass the name to the `Name` parameter. Wildcards '*' are supported. If an application group with the name doesn't exist, you'll get nothing back.
     
    Uses the BuildMaster native API, which can change without notice between releases. By default, this function returns *all* application groups.
     
    .EXAMPLE
    Get-BMApplicationGroup -Session $session
 
    Demonstrates how to get all BuildMaster application groups.
     
    .EXAMPLE
    Get-BMApplicationGroup -Session $session -Name 'My Application Group'
 
    Demonstrates how to get a specific application group.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [object]
        # The session to use when connecting to BuildMaster. Use `New-BMSession` to create session objects.
        $Session,
        
        [string]
        # The name of the application group to get. By default, all groups are returned.
        $Name
    )
    
    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
    $WhatIfPreference = $false
    
    $parameters = @{}
    
    Invoke-BMNativeApiMethod -Session $Session -Name 'ApplicationGroups_GetApplicationGroups' -Parameter $parameters -Method Post |
        Where-Object {
            if( $Name )
            {
                return $_.ApplicationGroup_Name -like $Name
            }
            return $true
        }
}



function Get-BMDeployment
{
    <#
    .SYNOPSIS
    Gets a deployment from BuildMaster.
 
    .DESCRIPTION
    The `Get-BMDeployment` function gets deployments in BuildMaster. It uses the [Release and Package Deployment API](http://inedo.com/support/documentation/buildmaster/reference/api/release-and-package).
 
    To get a specific deployment, pass a deploymentID.
 
    To get all the deployments for a specific package, pass a package object to the `Package` parameter (a package object must have a `packageId` or `packageNumber` property).
 
    To get all the deployments for a specific release, pass a release object to the `Release` parameter (a release object must have a `releaseId` or `releaseName` property).
 
    To get all the deployments for a specific application, pass an application object to the `Application` parameter (an application object must have an `applicationId` or `applicationName` property).
 
    .EXAMPLE
    Get-BMDeployment -Session $session
 
    Demonstrates how to get all deployments from the instance of BuildMaster.
 
    .EXAMPLE
    Get-BMDeployment -Session $session -ID $deployment
 
    Demonstrates how to get a specific deployment by passing a deployment object to the `Deployment` parameter. The `Get-BMDeployment` function looks for an `id` property on the object.
 
    .EXAMPLE
    Get-BMDeployment -Session $session -Package $package
 
    Demonstrates how to get all deployments for a package by passing a package object to the `Package` parameter. The `Get-BMDeployment` function looks for an `id` or `number` property on the object.
 
    .EXAMPLE
    Get-BMDeployment -Session $session -Release $release
 
    Demonstrates how to get all deployments for a release by passing a release object to the `Release` parameter. The `Get-BMDeployment` function looks for an `id` or `name` property on the object.
 
    .EXAMPLE
    Get-BMDeployment -Session $session -Application $app
 
    Demonstrates how to get all deployments for an application by passing an application object to the `Application` parameter. The `Get-BMDeployment` function looks for an `id` or `name` property on the object.
 
    .EXAMPLE
    Get-BMDeployment -Session $session -Application 34
 
    Demonstrates how to get all deployments for an application by passing its ID to the `Application` parameter.
    #>

    [CmdletBinding(DefaultParameterSetName='AllDeployments')]
    param(
        [Parameter(Mandatory)]
        [object]
        # A session object that contains the settings to use to connect to BuildMaster. Use `New-BMSession` to create session objects.
        $Session,

        [Parameter(Mandatory,ParameterSetName='ByDeployment')]
        [int]
        # The deployment to get. You can pass:
        #
        # * A deployment ID (as an integer)
        $ID,

        [Parameter(Mandatory,ParameterSetName='ByPackage')]
        [object]
        # The package whose deployments to get. You can pass:
        #
        # * A package ID as an integer
        # * A package name/number as a string.
        $Package,

        [Parameter(Mandatory,ParameterSetName='ByRelease')]
        [object]
        # The release whose deployments to get. You can pass:
        #
        # * The release ID as an integer.
        # * The release name as a string.
        $Release,

        [Parameter(Mandatory,ParameterSetName='ByApplication')]
        [object]
        # The application whose deployments to get. You can pass:
        #
        # * The application ID as an integer.
        # * The application name as a string.
        $Application
    )

    process
    {
        Set-StrictMode -Version 'Latest'
        Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

        $parameter = @{ }
        if( $PSCmdlet.ParameterSetName -eq 'ByDeployment' )
        {
            $parameter | Add-BMObjectParameter -Name 'deployment' -Value $ID
        }
        elseif( $PSCmdlet.ParameterSetName -eq 'ByPackage' )
        {
            if( $Package -is [string] )
            {
                $parameter['packageNumber'] = $Package
            }
            else
            {
                $parameter | Add-BMObjectParameter -Name 'package' -Value $Package
            }
        }
        elseif( $PSCmdlet.ParameterSetName -eq 'ByRelease' )
        {
            $parameter | Add-BMObjectParameter -Name 'release' -Value $Release
        }
        elseif( $PSCmdlet.ParameterSetName -eq 'ByApplication' )
        {
            $parameter | Add-BMObjectParameter -Name 'application' -Value $Application
        }

        Invoke-BMRestMethod -Session $Session -Name 'releases/packages/deployments' -Parameter $parameter -Method Post
    }
}



function Get-BMEnvironment
{
    <#
    .SYNOPSIS
    Returns environments from a BuildMaster instance.
 
    .DESCRIPTION
    The `Get-BMEnvironment` function gets all the environments from an instance of BuildMaster. By default, this function returns all active environments. Use the `Force` switch to return inactive environments, too.
     
    To return a specific environment (even one that's inactive), pass its name to the `Name` parameter. If an environment with the given name doesn't exist, you'll get an error. You can use wildcards to search for active environments. When searching for an environment with wildcards, inactive environments are not searched. Use the `Force` switch to include inactive environments in the search.
 
    Pass a session object representing the instance of BuildMaster to use to the `Session` parameter. Use `New-BMSession` to create a session object.
 
    This function uses BuildMaster's native APIs.
 
    .EXAMPLE
    Get-BMEnvironment
 
    Demonstrates how to return a list of all BuildMaster active environments.
 
    .EXAMPLE
    Get-BMEnvironment -Force
 
    Demonstrates how to return a list of all active *and* inactive BuildMaster environments.
 
    .EXAMPLE
    Get-BMEnvironment -Name '*Dev*'
 
    Demonstrates how to use wildcards to search for active environments.
 
    .EXAMPLE
    Get-BMEnvironment -Name '*Dev*' -Force
 
    Demonstrates how to use wildcards to search for active *and* inactive environments.
    #>

    [CmdletBinding(DefaultParameterSetName='All')]
    param(
        [Parameter(Mandatory)]
        # An object representing the instance of BuildMaster to connect to. Use `New-BMSession` to create session objects.
        [object]$Session,

        [Parameter(Mandatory,ParameterSetName='Name')]
        # The name of the environment to return. If one doesn't exist, you'll get an error. You may search for environments by using wildcards. If no environments match the wildcard pattern, no error is returned. When searching with wildcards, only active environments are searched. Use the `Force` switch to also search inactive environments.
        [string]$Name,

        # By default, inactive/disabled environments are not returned. Use the `Force` to return inactive environments, too.
        [Switch]$Force
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $environments = $null

    $wildcardSearch = [wildcardpattern]::ContainsWildcardCharacters($Name)

    # The Native API uses POST, which is interpreted as a data-modification, so
    # is subject to the user's WhatIfPreference. Since we know this method
    # doesn't modify data, disable WhatIf.
    $WhatIfPreference = $false

    # There's no way to get inactive environments from BuildMaster's infrastructure API so we have to use the native API. :(
    Invoke-BMNativeApiMethod -Session $session -Name 'Environments_GetEnvironments' -Parameter @{ IncludeInactive_Indicator = $true } -Method Post |
        ForEach-Object {
            [pscustomobject]@{
                                id = $_.Environment_Id;
                                name = $_.Environment_Name;
                                active = $_.Active_Indicator;
                                parentName = $_.Parent_Environment_Name;
                            }
        } |
        Where-Object {
            # Only return environments that match the user's search.
            if( $Name )
            {
                return $_.name -like $Name
            }
            return $true
        } |
        Where-Object {
            # Only return active environments unless the user is using the Force or retrieving a specific environment.
            if( $Force -or ($Name -and -not $wildcardSearch) )
            {
                return $true
            }
            return $_.active
        } |
        Tee-Object -Variable 'environments'
    
    if( $PSCmdlet.ParameterSetName -eq 'All' -or $environments )
    {
        return
    }
    
    if( -not $wildcardSearch )
    {
        Write-Error -Message ('Environment "{0}" does not exist.' -f $Name) -ErrorAction $ErrorActionPreference
    }
}


function Get-BMPackage
{
    <#
    .SYNOPSIS
    Gets a package from BuildMaster.
 
    .DESCRIPTION
    The `Get-BMPackage` function gets a package from BuildMaster. With no parameters, it returns all packages. To get all the packages that are part of a release, pass a release object or ID to the `Release` property (a release object must have an `id` or `Release_Id` property). To get a specific package, pass a package object or ID to the `Package` property (a package object must have an `id` or `Package_Id` property).
 
    This function uses BuildMaster's "Release & Package Deployment" API. When creating a BuildMaster session (using the `New-BMSession` function), the API key you use must have access to that API.
 
    .EXAMPLE
    Get-BMPackage -Session $session
 
    Demonstrates how to get all packages.
 
    .EXAMPLE
    Get-BMPackage -Session $session -Package $package
 
    Demonstrates how to get a specific package using a package object. The package object must have an `id` or `Package_Id` property.
 
    .EXAMPLE
    Get-BMPackage -Session $session -Package 500
 
    Demonstrates how to get a specific package using its ID.
 
    .EXAMPLE
    Get-BMPackage -Session $session -Release $release
 
    Demonstrates how to get all the packages that are part of a release using a release object. The release object must have an `id` or `Release_Id` property.
 
    .EXAMPLE
    Get-BMPackage -Session $session -Release 438
 
    Demonstrates how to get all the packages that are part of a release using the release's ID.
    #>

    [CmdletBinding(DefaultParameterSetName='AllPackages')]
    param(
        [Parameter(Mandatory=$true)]
        [object]
        # A object that represents what instance of BuildMaster to connect to. Use the `New-BMSession` function to create session objects.
        $Session,

        [Parameter(Mandatory=$true,ParameterSetName='SpecificPackage')]
        [object]
        # The package to get. Can be:
        #
        # * A package object with a `Package_Id` or `id` property.
        # * A package ID (as an integer)
        $Package,

        [Parameter(Mandatory=$true,ParameterSetName='ReleasePackages')]
        [object]
        # The release whose packages to get. Gets all the packages that are part of this release.
        #
        # * A release object with a `Release_Id` or `id` property.
        # * A release ID (as an integer)
        $Release
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $parameter = $null
    if( $PSCmdlet.ParameterSetName -eq 'SpecificPackage' )
    {
        $parameter = @{ } | Add-BMObjectParameter -Name 'package' -Value $Package -PassThru
    }
    elseif( $PSCmdlet.ParameterSetName -eq 'ReleasePackages' )
    {
        $parameter = @{ } | Add-BMObjectParameter -Name 'release' -Value $Release -PassThru
    }

    $parameterParam = @{ }
    if( $parameter )
    {
        $parameterParam['Parameter'] = $parameter
    }

    Invoke-BMRestMethod -Session $Session -Name 'releases/packages' @parameterParam -Method Post |
        Where-Object {
            # There's a bug in BuildMaster's API that returns packages for multiple releases. We don't want this.
            if( $PSCmdlet.ParameterSetName -eq 'ReleasePackages' )
            {
                return $_.releaseId -eq $parameter.releaseId
            }
            return $true
        }
}



function Get-BMPipeline
{
    <#
    .SYNOPSIS
    Gets pipelines.
 
    .DESCRIPTION
    The `Get-BMPipeline` function gets pipelines. By default, it returns all pipelines. To get a specific pipeline, pass its name to the `Name` parameter or its ID to the `ID` parameter. The `Name` parameter supports wildcards. To get a specific application's pipelines, pass the application's ID to the `ApplicationID` parameter.
 
    This function uses the `Pipelines_GetPipelines` and `Pipelines_GetPipeline` native API methods.
 
    .EXAMPLE
    Get-BMPipeline -Session $session
 
    Demonstrates how to get all the pipelines.
 
    .EXAMPLE
    Get-BMPipeline -Session $session -Name 'BuildMaster Automation'
 
    Demonstrates how to get pipelines by name. If there are multiple pipelines with the same name, they will all be returned.
 
    .EXAMPLE
    Get-BMPipeline -Session $session -Name '*Automation'
 
    Demonstrates that you can use wildcards in the `Name` parameter's value to search for pipelines.
     
    .EXAMPLE
    Get-BMPipeline -Session $session -ID 34
 
    Demonstrates how to get a specific pipeline by its ID.
     
    .EXAMPLE
    Get-BMPipeline -Session $session -ApplicationID 39
 
    Demonstrates how to get a specific application's pipelines.
     
    .EXAMPLE
    Get-BMPipeline -Session $session -ApplicationID 39 -Name 'Pipeline 2'
 
    Demonstrates how to get an application's pipeline by its name.
    #>

    [CmdletBinding(DefaultParameterSetName='Searching')]
    param(
        [Parameter(Mandatory=$true)]
        [object]
        # An object that represents the instance of BuildMaster to connect to. Use the `New-BMSession` function to creates a session object.
        $Session,

        [Parameter(ParameterSetName='Searching')]
        [string]
        # The name of the pipeline to get. Supports wildcards.
        $Name,

        [Parameter(ParameterSetName='Searching')]
        [int]
        # The ID of the application whose pipelines to get. The default is to return all pipelines.
        $ApplicationID,

        [Parameter(Mandatory=$true,ParameterSetName='SpecificPipeline')]
        [int]
        # The ID of the pipeline to get.
        $ID
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
    $WhatIfPreference = $false

    $parameter = @{ }
    $methodName = 'Pipelines_GetPipelines'
    if( $PSCmdlet.ParameterSetName -eq 'SpecificPipeline' )
    {
        $methodName = 'Pipelines_GetPipeline'
        $parameter['Pipeline_Id'] = $ID
    }
    else
    {
        if( $ApplicationID )
        {
            $parameter['Application_Id'] = $ApplicationID
        }
    }

    $optionalParams = @{ }
    if( $parameter.Count )
    {
        $optionalParams['Parameter'] = $parameter
        $optionalParams['Method'] = 'Post'
    }

    Invoke-BMNativeApiMethod -Session $session -Name $methodName @optionalParams |
        Where-Object {
            if( $Name )
            {
                return ($_.Pipeline_Name -like $Name)
            }
            
            return $true
        }
}



function Get-BMRelease
{
    <#
    .SYNOPSIS
    Gets the release for an application in BuildMaster.
 
    .DESCRIPTION
    The `Get-BMRelease` function gets releases in BuildMaster. It uses the [Release and Package Deployment API](http://inedo.com/support/documentation/buildmaster/reference/api/release-and-package).
 
    To get a specific release, pass a release object, release ID, or release name to the `Release` parameter.
 
    To get all the releases for a specific application, pass an application object, application ID, or application name to the `Application` parameter. You can get a specific application's release by passing the release's name to the `Name` parameter.
 
    .EXAMPLE
    Get-BMRelease -Session $session -Release $release
 
    Demonstrates how to get a specific release by passing a release object to the `Release` parameter. The `Get-BMRelease` function looks for an `id` or `name` property on the object.
 
    .EXAMPLE
    Get-BMRelease -Session $session -Application $app
 
    Demonstrates how to get all the releases for an application by passing an application object to the `Application` parameter. The application object must have a`Application_Id`, `id`, `Application_Name`, or `name` properties.
 
    .EXAMPLE
    Get-BMRelease -Session $session -Application 34
 
    Demonstrates how to get all the releases for an application by passing its ID to the `Application` parameter.
 
    .EXAMPLE
    Get-BMRelease -Session $session -Application 'BuildMasterAutomation'
 
    Demonstrates how to get all the releases for an application by passing its name to the `Application` parameter.
 
    .EXAMPLE
    Get-BMRelease -Session $session -Application 'BuildMasterAutomation' -Name '4.1'
 
    Demonstrates how to get a specific release for an application by passing the release's name to the `Name` parameter. In this example, the '4.1' release will be returned, if it exists.
    #>

    [CmdletBinding(DefaultParameterSetName='AllReleases')]
    param(
        [Parameter(Mandatory=$true)]
        [object]
        # A session object that contains the settings to use to connect to BuildMaster. Use `New-BMSession` to create session objects.
        $Session,

        [Parameter(Mandatory=$true,ValueFromPipeline=$true,ParameterSetName='ByRelease')]
        [object]
        # The release to get. You can pass:
        #
        # * A release object. It must have either an `id` or `name` property.
        # * The release ID as an integer.
        # * The release name as a string.
        $Release,

        [Parameter(Mandatory=$true,ParameterSetName='ByApplication')]
        [object]
        # The application whose releases to get. You can pass:
        #
        # * An application object. It must have `Application_Id`, `id`, `Application_Name`, or `name` properties.
        # * The application ID as an integer.
        # * The application name as a string.
        $Application,

        [string]
        # The name of the release to get.
        $Name
    )

    process
    {
        Set-StrictMode -Version 'Latest'
        Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

        $parameter = @{ } 
        
        if( $PSCmdlet.ParameterSetName -eq 'ByRelease' )
        {
            $parameter | Add-BMObjectParameter -Name 'release' -Value $Release
        }
        elseif( $PSCmdlet.ParameterSetName -eq 'ByApplication' )
        {
            $parameter | Add-BMObjectParameter -Name 'application' -Value $Application
        }

        if( $Name )
        {
            $parameter['releaseName'] = $Name
        }

        Invoke-BMRestMethod -Session $Session -Name 'releases' -Parameter $parameter -Method Post
    }
}


function Get-BMServer
{
    <#
    .SYNOPSIS
    Returns servers in BuildMaster.
 
    .DESCRIPTION
    The `Get-BMServer` function gets all the servers from an instance of BuildMaster. By default, this function returns all servers. To return a specific server, pass its name to the `Name` parameter. If a server with that name doesn't exist, you'll get an error. The `Name` parameter supports wildcards. If you search with wildcards and a server doesn't exist, you won't get any errors.
 
    The BuildMaster API returns plaintext versions of a server's API key (if it is using AES encryption). This function converts those keys into `SecureString`s to make it harder to accidentally view/save them.
 
    This function uses BuildMaster's infrastructure management API.
 
    Pass a session object representing the instance of BuildMaster to use to the `Session` parameter. Use `New-BMSession` to create a session object.
 
    .EXAMPLE
    Get-BMServer
 
    Demonstrates how to return a list of all BuildMaster servers.
 
    .EXAMPLE
    Get-BMServer -Name '*example*'
 
    Demonstrates how to use wildcards to search for a server.
    #>

    [CmdletBinding(DefaultParameterSetName='All')]
    param(
        [Parameter(Mandatory)]
        # An object representing the instance of BuildMaster to connect to. Use `New-BMSession` to create session objects.
        [object]$Session,

        [Parameter(Mandatory,ParameterSetName='Name')]
        # The name of the server to return. Wildcards supported. By default, all servers are returned.
        [string]$Name
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $servers = $null

    Invoke-BMRestMethod -Session $Session -Name 'infrastructure/servers/list' |
        Where-Object {
            if( $Name )
            {
                $_.name -like $Name
            }
            else
            {
                return $true
            }
        } |
        Add-PSTypeName -Server |
        ForEach-Object {
            $server = $_
            foreach( $memberName in @( 'name', 'roles', 'environments', 'serverType', 'hostName', 'port', 'encryptionType', 'encryptionKey', 'requireSsl', 'credentialsName', 'tempPath', 'wsManUrl', 'active', 'variables' ) )
            {
                if( -not ($server | Get-Member -Name $memberName) ) 
                {
                    $server | Add-Member -MemberType NoteProperty -Name $memberName -Value $null
                }
            }

            if( $server.encryptionKey )
            {
                $server.encryptionKey = ConvertTo-SecureString -String $_.encryptionKey -AsPlainText -Force
            }
            $server
        } |
        Tee-Object -Variable 'servers' 

    if( $PSCmdlet.ParameterSetName -eq 'All' -or $servers )
    {
        return
    }
    
    if( -not [wildcardpattern]::ContainsWildcardCharacters($Name) )
    {
        Write-Error -Message ('Server "{0}" does not exist.' -f $Name) -ErrorAction $ErrorActionPreference
    }
}


function Get-BMServerRole
{
    <#
    .SYNOPSIS
    Returns the server roles.
 
    .DESCRIPTION
    The `Get-BMServerRole` function gets all the server roles from an instance of BuildMaster. By default, this function returns all server roles. To return a specific role, pass its name to the `Name` parameter. The `Name` parameter supports wildcards. If a server role doesn't exist, you'll get an error.
 
    This function uses BuildMaster's infrastructure management API.
 
    Pass a session object representing the instance of BuildMaster to use to the `Session` parameter. Use `New-BMSession` to create a session object.
 
    .EXAMPLE
    Get-BMServerRole
 
    Demonstrates how to return a list of all BuildMaster server roles.
 
    .EXAMPLE
    Get-BMServerRole -Name '*Service*'
 
    Demonstrates how to use wildcards to search for a service role.
    #>

    [CmdletBinding(DefaultParameterSetName='All')]
    param(
        [Parameter(Mandatory)]
        # An object representing the instance of BuildMaster to connect to. Use `New-BMSession` to create session objects.
        [object]$Session,

        [Parameter(Mandatory,ParameterSetName='Name')]
        # The name of the role to return. Wildcards supported. By default, all roles are returned.
        [string]$Name
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $roles = $null

    Invoke-BMRestMethod -Session $Session -Name 'infrastructure/roles/list' |
        Where-Object {
            if( $Name )
            {
                $_.name -like $Name
            }
            else
            {
                return $true
            }
        } |
        Tee-Object -Variable 'roles'

    if( $PSCmdlet.ParameterSetName -eq 'All' -or $roles )
    {
        return
    }
    
    if( -not [wildcardpattern]::ContainsWildcardCharacters($Name) )
    {
        Write-Error -Message ('Server role "{0}" does not exist.' -f $Name) -ErrorAction $ErrorActionPreference
    }
}


function Get-BMVariable
{
    <#
    .SYNOPSIS
    Gets BuildMaster variables.
 
    .DESCRIPTION
    The `Get-BMVariable` function gets BuildMaster variables. By default, it gets all global variables. It can also get all variables for a specific environment, server, server role, application group, and application variables.
 
    To get a specific variable, pass the variable's name to the `Name` parameter. The default is to return all variables for the specific entity you've chosen (the default is global variables).
     
    To get an environment's variables, pass the environment's name to the `EnvironmentName` parameter.
 
    To get a server role's variables, pass the server role's name to the `ServerRoleName` parameter.
 
    To get a server's variables, pass the server's name to the `ServerName` parameter.
 
    To get an application group's variables, pass the application group's name to the `ApplicationGroupName` parameter.
 
    To get an application's variables, pass the application's name to the `ApplicationName` parameter.
 
    Pass a session object representing the instance of BuildMaster to use to the `Session` parameter. Use `New-BMSession` to create a session object.
 
    This function uses BuildMaster's variables API. Due to a bug in BuildMaster, it uses the native API when reading application group and application variables.
 
    .EXAMPLE
    Get-BMVariable
 
    Demonstrates how to get all global variables.
 
    .EXAMPLE
    Get-BMVariable -Session $session -Name 'Var'
 
    Demonstrates how to get a specific global variable.
 
    .EXAMPLE
    Get-BMVariable -Session $session -EnvironmentName 'Dev'
 
    Demonstrates how to all an environment's variables.
 
    .EXAMPLE
    Get-BMVariable -Session $session -Name 'Var' -EnvironmentName 'Dev'
 
    Demonstrates how to get a specific variable in an environment.
 
    .EXAMPLE
    Get-BMVariable -Session $session -ServerRoleName 'WebApp'
 
    Demonstrates how to get all variables in a specific server role.
 
    .EXAMPLE
    Get-BMVariable -Session $session -Name 'Var' -ServerRoleName 'WebApp'
 
    Demonstrates how to get a specific variable in a server role.
 
    .EXAMPLE
    Get-BMVariable -Session $session -ServerName 'example.com'
 
    Demonstrates how to get all variables for a specific server.
 
    .EXAMPLE
    Get-BMVariable -Session $session -Name 'Var' -ServerName 'example.com'
 
    Demonstrates how to get a specific variable in a server.
 
    .EXAMPLE
    Get-BMVariable -Session $session -ApplicationGroupName 'WebApps'
 
    Demonstrates how to get all variables from a specific application group.
 
    .EXAMPLE
    Get-BMVariable -Session $session -Name 'Var' -ApplicationGroupName 'WebApps'
 
    Demonstrates how to get a specific variable from an application group.
 
    .EXAMPLE
    Get-BMVariable -Session $session -ApplicationName 'www'
 
    Demonstrates how to get all variables from a specific application.
 
    .EXAMPLE
    Get-BMVariable -Session $session -Name 'Var' -ApplicationName 'www'
 
    Demonstrates how to get a specific variable from an application.
    #>

    [CmdletBinding(DefaultParameterSetName='global')]
    param(
        [Parameter(Mandatory)]
        # An object representing the instance of BuildMaster to connect to. Use `New-BMSession` to create session objects.
        [object]$Session,

        # The name of the variable to get. The default is to get all global variables.
        [string]$Name,

        [Parameter(Mandatory,ParameterSetName='application')]
        # The name of the application where the variable or variables should be read. The default is to get global variables.
        [string]$ApplicationName,

        [Parameter(Mandatory,ParameterSetName='application-group')]
        # The name of the application group where the variable or variables should be read. The default is to get global variables.
        [string]$ApplicationGroupName,

        [Parameter(Mandatory,ParameterSetName='environment')]
        # The name of the environment where the variable or variables should be read. The default is to get global variables.
        [string]$EnvironmentName,

        [Parameter(Mandatory,ParameterSetName='server')]
        # The name of the server where the variable or variables should be read. The default is to get global variables.
        [string]$ServerName,

        [Parameter(Mandatory,ParameterSetName='role')]
        # The name of the server role where the variable or variables should be read. The default is to get global variables.
        [string]$ServerRoleName,

        # Return the variable's value, not an object representing the variable.
        [Switch]$ValueOnly
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $entityParamNames = @{
                            'application' = 'ApplicationName';
                            'application-group' = 'ApplicationGroupName';
                            'environment' = 'EnvironmentName';
                            'server' = 'ServerName';
                            'role' = 'ServerRoleName';
                        }

    $endpointName = 'variables/{0}' -f $PSCmdlet.ParameterSetName
    $entityName = ''
    if( $PSCmdlet.ParameterSetName -ne 'global' )
    {
        $entityParamName = $entityParamNames[$PSCmdlet.ParameterSetName]
        $entityName = $PSBoundParameters[$entityParamName]
        $encodedEntityName = [uri]::EscapeDataString($entityName)
        $endpointName = '{0}/{1}' -f $endpointName,$encodedEntityName
    }

    if( $PSCmdlet.ParameterSetName -in @( 'application', 'application-group' ) )
    {
        $nativeVariables = @()
        $originalWhatIf = $WhatIfPreference
        $WhatIfPreference = $false
        try
        {
            if( $PSCmdlet.ParameterSetName -eq 'application' )
            {
                $app = Get-BMApplication -Session $Session -Name $ApplicationName
                if( -not $app )
                {
                    Write-Error -Message ('Application "{0}" does not exist.' -f $ApplicationName) -ErrorAction $ErrorActionPreference
                    return
                }
                $nativeVariables = Invoke-BMNativeApiMethod -Session $Session -Name 'Variables_GetVariablesForScope' -Method Post -Parameter @{ 'Application_Id' = $app.Application_Id } 
            }
            elseif( $PSCmdlet.ParameterSetName -eq 'application-group' )
            {
                $appGroup = Get-BMApplicationGroup -Session $Session -Name $ApplicationGroupName
                if( -not $appGroup )
                {
                    Write-Error -Message ('Application group "{0}" does not exist.' -f $ApplicationGroupName) -ErrorAction $ErrorActionPreference
                    return
                }
                $nativeVariables = Invoke-BMNativeApiMethod -Session $Session -Name 'Variables_GetVariablesForScope' -Method Post -Parameter @{ 'ApplicationGroup_Id' = $appGroup.ApplicationGroup_Id } 
            }
        }
        finally
        {
            $WhatIfPreference = $originalWhatIf
        }
        $appValues = @{ }
        $nativeVariables |
            ForEach-Object {
                $bytes = [Convert]::FromBase64String($_.Variable_Value)
                $appValues[$_.Variable_Name] = [Text.Encoding]::UTF8.GetString($bytes)
            }
        $values = [pscustomobject]$appValues
    }
    else
    {
        $values = Invoke-BMRestMethod -Session $Session -Name $endpointName
    }

    if( -not $values )
    {
        return
    }

    $foundVars = $null

    # Tee-Object supports WhatIfPreference.
    $originalWhatIf = $WhatIfPreference
    $WhatIfPreference = $false
    try
    {
        $values | 
            Get-Member -MemberType NoteProperty |
            ForEach-Object {
                $variableName = $_.Name
                [pscustomobject]@{ 
                                    Name = $variableName;
                                    Value = $values.$variableName
                                }
            } |
            Where-Object {
                if( $Name )
                {
                    return $_.Name -like $Name
                }
                return $true
            } |
            ForEach-Object {
                if( $ValueOnly )
                {
                    return $_.Value
                }
                return $_
            } | 
            Tee-Object -Variable 'foundVars'
    }
    finally
    {
        $WhatIfPreference = $originalWhatIf
    }

    if( $Name -and -not [wildcardpattern]::ContainsWildcardCharacters($Name) -and -not $foundVars )
    {
        $entityTypeDescriptions = @{
                                    'application' = 'application';
                                    'application-group' = 'application group';
                                    'environment' = 'environment';
                                    'server' = 'server';
                                    'role' = 'server role'
                              }

        $typeName = $entityTypeDescriptions[$PSCmdlet.ParameterSetName]
        if( $typeName )
        {
            $typeName = ' in {0}' -f $typeName
        }

        $entityNameDesc = ''
        if( $entityName )
        {
            $entityNameDesc = ' "{0}"' -f $entityName
        }
        Write-Error -Message ('Variable "{0}"{1}{2} does not exist.' -f $Name,$typeName,$entityNameDesc) -ErrorAction $ErrorActionPreference
    }
}


function Invoke-BMNativeApiMethod
{
    <#
    .SYNOPSIS
    Calls a method on BuildMaster's "native" API.
 
    .DESCRIPTION
    The `Invoke-BMNativeApiMethod` calls a method on BuildMaster's "native" API. From Inedo:
 
    > This API endpoint should be avoided if there is an alternate API endpoint available, as those are much easier to use and will likely not change.
 
    In other words, use a native API at your own peril.
 
    When using the `WhatIf` parameter, only web requests that use the `Get` HTTP method are made.
 
    .EXAMPLE
    Invoke-BMNativeApiMethod -Session $session -Name 'Applications_CreateApplication' -Parameter @{ Application_Name = 'fubar' }
 
    Demonstrates how to call `Invoke-BMNativeApiMethod`. In this example, it is calling the `Applications_CreateApplication` method to create a new application named `fubar`.
    #>

    [CmdletBinding(SupportsShouldProcess=$true)]
    param(
        [Parameter(Mandatory=$true)]
        [object]
        # A session object that represents the BuildMaster instance to use. Use the `New-BMSession` function to create session objects.
        $Session,

        [Parameter(Mandatory=$true)]
        [string]
        # The name of the API method to use. The list can be found at http://inedo.com/support/documentation/buildmaster/reference/api/native, or under your local BuildMaster instance at /reference/api
        $Name,

        [Microsoft.PowerShell.Commands.WebRequestMethod]
        # The HTTP/web method to use. The default is `GET`.
        $Method = [Microsoft.PowerShell.Commands.WebRequestMethod]::Get,

        [hashtable]
        # Any parameters to pass to the endpoint. The keys/values are sent in the body of the request as a JSON object.
        $Parameter
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $parameterParam = @{ }
    if( $Parameter -and $Parameter.Count )
    {
        $parameterParam['Parameter'] = $Parameter
        $parameterParam['AsJson'] = $true
    }

    Invoke-BMRestMethod -Session $Session -Name ('json/{0}' -f $Name) -Method $Method @parameterParam
}


function Invoke-BMRestMethod
{
    <#
    .SYNOPSIS
    Invokes a BuildMaster REST method.
 
    .DESCRIPTION
    The `Invoke-BMRestMethod` invokes a BuildMaster REST API method. You pass the path to the endpoint (everything after `/api/`) via the `Name` parameter, the HTTP method to use via the `Method` parameter, and the parameters to pass in the body of the request via the `Parameter` parameter. This function converts the `Parameter` hashtable to a URL-encoded query string and sends it in the body of the request. You can send the parameters as JSON by adding the `AsJson` parameter. You can pass your own custom body to the `Body` parameter. If you do, make sure you set an appropriate content type for the request with the `ContentType` parameter.
 
    You also need to pass an object that represents the BuildMaster instance and API key to use when connecting via the `Session` parameter. Use the `New-BMSession` function to create a session object.
 
    When using the `WhatIf` parameter, only web requests that use the `Get` HTTP method are made.
    #>

    [CmdletBinding(SupportsShouldProcess=$true,DefaultParameterSetName='NoBody')]
    param(
        [Parameter(Mandatory=$true)]
        [object]
        # A session object that represents the BuildMaster instance to use. Use the `New-BMSession` function to create session objects.
        $Session,

        [Parameter(Mandatory=$true)]
        [string]
        # The name of the API to use. The should be everything after `/api/` in the method's URI.
        $Name,

        [Microsoft.PowerShell.Commands.WebRequestMethod]
        # The HTTP/web method to use. The default is `GET`.
        $Method = [Microsoft.PowerShell.Commands.WebRequestMethod]::Get,

        [Parameter(Mandatory=$true,ParameterSetName='BodyFromHashtable')]
        [hashtable]
        # That parameters to pass to the method. These are converted to JSON and sent to the API in the body of the request.
        $Parameter,

        [Parameter(ParameterSetName='BodyFromHashtable')]
        [Switch]
        # Send the request as JSON. Otherwise, the data is sent as name/value pairs.
        $AsJson,

        [Parameter(Mandatory=$true,ParameterSetName='CustomBody')]
        [string]
        # The body to send.
        $Body,

        [string]
        # The content type of the web request.
        #
        # By default,
        #
        # * if passing a value to the `Parameter` parameter, the content type is set to `application/x-www-form-urlencoded`
        # * if passing a value to the `Parameter` parameter and you're using the `AsJson` switch, the content type is set to `application/json`.
        #
        # Otherwise, the content type is not set. If you're passing your own body to the `Body` parameter, you may have to set the appropriate content type for BuildMaster to respond.
        $ContentType
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $uri = '{0}api/{1}' -f $Session.Uri,$Name
    
    $debugBody = ''
    $webRequestParam = @{ }
    if( $Body )
    {
        $webRequestParam['Body'] = $Body
    }
    elseif( $Parameter )
    {
        if( $AsJson )
        {
            $Body = $Parameter | ConvertTo-Json -Depth 100
            $debugBody = $Body -replace '("API_Key": +")[^"]+','$1********'
            $encryptionKeyRegex = '"encryptionKey":( +)"([^"]+)"'
            if( $debugBody -match $encryptionKeyRegex )
            {
                $maskLength = $Matches[2].Length
                $mask = '*' * $maskLength
                $debugBody = $debugBody -replace $encryptionKeyRegex,('"encryptionKey":$1"{0}"' -f $mask)
            }
            if( -not $ContentType )
            {
                $ContentType = 'application/json; charset=utf-8'
            }
        }
        else
        {
            $keyValues = $Parameter.Keys | ForEach-Object { '{0}={1}' -f [Web.HttpUtility]::UrlEncode($_),[Web.HttpUtility]::UrlEncode($Parameter[$_]) }
            $Body = $keyValues -join '&'
            if( -not $ContentType )
            {
                $ContentType = 'application/x-www-form-urlencoded; charset=utf-8'
            }
            $debugBody = $Parameter.Keys | ForEach-Object {
                $value = $Parameter[$_]
                if( $_ -eq 'API_Key' )
                {
                    $value = '********'
                }
                ' {0}={1}' -f $_,$value }
        }
        $webRequestParam['Body'] = $Body
    }

    if( $ContentType )
    {
        $webRequestParam['ContentType'] = $ContentType
    }

    $headers = @{
                    'X-ApiKey' = $Session.ApiKey;
                }

    #$DebugPreference = 'Continue'
    Write-Debug -Message ('{0} {1}' -f $Method.ToString().ToUpperInvariant(),($uri -replace '\b(API_Key=)([^&]+)','$1********'))
    if( $ContentType )
    {
        Write-Debug -Message (' Content-Type: {0}' -f $ContentType)
    }
    foreach( $headerName in $headers.Keys )
    {
        $value = $headers[$headerName]
        if( $headerName -eq 'X-ApiKey' )
        {
            $value = '*' * 8
        }

        Write-Debug -Message (' {0}: {1}' -f $headerName,$value)
    }
    
    $debugBody | Write-Debug
    
    $numErrors = $Global:Error.Count
    try
    {
        if( $Method -eq [Microsoft.PowerShell.Commands.WebRequestMethod]::Get -or $PSCmdlet.ShouldProcess($Uri,$Method) )
        {
            Invoke-RestMethod -Method $Method -Uri $uri @webRequestParam -Headers $headers | 
                ForEach-Object { $_ } 
        }
    }
    catch [Net.WebException]
    {
        if( $ErrorActionPreference -eq 'Ignore' )
        {
            for( $idx = $numErrors; $idx -lt $Global:Error.Count; ++$idx )
            {
                $Global:Error.RemoveAt(0)
            }
        }
        Write-Error -ErrorRecord $_ -ErrorAction $ErrorActionPreference
    }
}


function New-BMApplication
{
    <#
    .SYNOPSIS
    Creates an application in BuildMaster.
 
    .DESCRIPTION
    The `New-BMApplication` function creates an application in BuildMaster. This function uses the native BuildMaster API, which can change without notice between releases. Only a name is required to create an application. The name must be unique and not in use.
 
    These parameters are also available:
 
    * `ReleaseNumberScheme`: sets the release number scheme to use when you create a new release for the application. Options are `MajorMinorRevision`, `MajorMinor`, or `DateBased`.
    * `BuildNumberScheme`: sets the build number scheme to use when creating new packages/builds for an application. Options are `Unique`, `Sequential`, `DateBased`.
    * `AllowMultipleActiveBuilds`: a flag that indicates if the application is allowed to have multiple active builds.
 
    .EXAMPLE
    New-BMApplication -Session $session -Name 'MyNewApplication'
 
    Demonstrates the simplest way to create an application. In this example, a `MyNewApplication` application will be created and all its fields set to BuildMaster's default values.
 
    .EXAMPLE
    New-BMApplication -Session $session -Name 'MyNewApplication' -ReleaseNumberSchemeName MajorMinor -BuildNumberSchemeName Sequential -AllowMultipleActiveBuilds
 
    This example demonstrates all the fields you can set when creating a new application. In this example, the new application will be called `MyNewApplication`, its release number scheme will be `MajorMinor`, its build number schema will be `Sequential`, it will allow multiple active releases, and it will allow multiple active builds.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [object]
        # A session object that represents the BuildMaster instance to use. Use the `New-BMSession` function to create session objects.
        $Session,

        [Parameter(Mandatory=$true)]
        [string]
        # The name of the application.
        $Name,

        [string]
        [ValidateSet('MajorMinorRevision','MajorMinor','DateBased')]
        # The name of the release number scheme. Should be one of:
        #
        # * `MajorMinorRevision`
        # * `MajorMinor`
        # * `DateBased`
        $ReleaseNumberSchemeName,

        [string]
        [ValidateSet('Unique','Sequential','DateTimeBased')]
        # The name of the build number scheme. Should be one of:
        #
        # * `Unique`
        # * `Sequential`
        # * `DateTimeBased`
        $BuildNumberSchemeName,

        [Switch]
        # Allow multiple active builds.
        $AllowMultipleActiveBuilds,

        [string]
        # The application group to assign. By default, the application will be ungrouped.
        $ApplicationGroupId
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $parameters = @{
                        'Application_Name' = $Name;
                    }
    if( $ReleaseNumberSchemeName )
    {
        $parameters['ReleaseNumber_Scheme_Name'] = $ReleaseNumberSchemeName;
    }

    if( $BuildNumberSchemeName )
    {
        $parameters['BuildNumber_Scheme_Name'] = $BuildNumberSchemeName;
    }

    if( $AllowMultipleActiveBuilds )
    {
        $parameters['AllowMultipleActiveBuilds_Indicator'] = $true;
    }

    if( $ApplicationGroupId )
    {
        $parameters['ApplicationGroup_Id'] = $ApplicationGroupId
    }
    
    $appID = Invoke-BMNativeApiMethod -Session $Session -Name 'Applications_CreateApplication' -Parameter $parameters -Method Post
    if( -not $appID )
    {
        return
    }

    Invoke-BMNativeApiMethod -Session $Session -Name 'Applications_GetApplication' -Parameter @{ 'Application_Id' = $appID } -Method Post |
        Select-Object -ExpandProperty 'Applications_Extended'
}


function New-BMEnvironment
{
    <#
    .SYNOPSIS
    Creates a new environment in a BuildMaster instance.
 
    .DESCRIPTION
    The `New-BMEnvironment` creates a new environment in BuildMaster. Pass the name of the environment to the `Name` parameter. Names may only contain letters, numbers, periods, underscores, or dashes and may not end with an underscore or dash.
     
    Every environment must have a unique name. If you create a environment with a duplicate name, you'll get an error.
 
    Environments can't be deleted. Deleted environments are just disabled/inactive. If you need to reactivate/enable a disabled environment, use `Enable-BMEnvironment`. If you try to create a new environment with the same name as an inactive environment, you'll get an error.
     
    Pass a session object representing the instance of BuildMaster to use to the `Session` parameter. Use the `New-BMSession` function to create session objects.
 
    This function uses BuildMaster's infrastructure management API.
 
    .EXAMPLE
    New-BMEnvironment -Session $session -Name 'DevNew'
 
    Demonstrates how to create a new environment.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory)]
        # An object representing the instance of BuildMaster to connect to. Use `New-BMSession` to create session objects.
        [Object]$Session,

        [Parameter(Mandatory)]
        [ValidatePattern('^[A-Za-z][A-Za-z0-9_-]*(?<![_-])$')]
        [ValidateLength(1,50)]
        # The name of the environment to create. Must contain only letters, numbers, underscores, or dashes. Must begin with a letter. Must not end with an underscore or dash. Must be between 1 and 50 characters long.
        [String]$Name,

        # The name of this environment's parent environemnt.
        [String]$ParentName,

        # By default, new environments are active. If you want the environment to be inactive, use this switch.
        [switch]$Inactive
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $parameter = @{
                    name = $Name;
                    parentName = $ParentName;
                    active = (-not $Inactive);
                 }
    $encodedName = [Uri]::EscapeDataString($Name)
    Invoke-BMRestMethod -Session $Session -Name ('infrastructure/environments/create/{0}' -f $encodedName) -Method Post -Parameter $parameter -AsJson
}


function New-BMPackage
{
    <#
    .SYNOPSIS
    Creates a new package for a release.
 
    .DESCRIPTION
    The `New-BMPackage` creates a new package/version/build of an application. In order to deploy an application, the application must have a release. Then you create packages in that release, and each package is then deployed using the release's pipeline.
 
    .EXAMPLE
    New-BMPackage -Session $session -Release $release
 
    Demonstrates how to create a new package in the `$release` release. BuildMaster detects what application based on the release (since releases are always tied to applications). Verion numbers and package numbers are incremented and handled based on the release settings.
 
    The `$release` parameter can be:
 
    * A release object with an `id` property.
    * A release ID integer.
 
    .EXAMPLE
    New-BMPackage -Session $session -ReleaseName '53' -Application $applicatoin
 
    Demonstrates how to create a new package by using the release's name. Since release names are only unique within an application, you must also specify the application via the `Application` parameter.
 
    .EXAMPLE
    New-BMPackage -Session $session -Release $release -PacakgeName '56.develop' -Variable @{ ProGetPackageName = '17.1.54+developer.deadbee' }
 
    Demonstrates how to create a release with a specific name, `56.develop`, and with a package-level variable, `ProGetPackageName`.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [object]
        # An object that represents the instance of BuildMaster to connect to. Use the `New-BMSession` function to creates a session object.
        $Session,

        [Parameter(Mandatory=$true,ParameterSetName='ByReleaseID')]
        [object]
        # The release where the package should be created. Can be:
        #
        # * a release object with an `id` property
        # * the release ID as an integer
        $Release,

        [Parameter(Mandatory=$true,ParameterSetName='ByReleaseNumber')]
        [string]
        # The release number where the package should be created. Release numbers are unique within an application and can be duplicated between applications. If you use this parameter to identify the release, you must also provide a value for the `Application` parameter.
        $ReleaseNumber,

        [Parameter(Mandatory=$true,ParameterSetName='ByReleaseNumber')]
        [object]
        # The application where the release identified by the `ReleaseNumber` parameter can be found. Can be:
        #
        # * An application object with a `Application_Id`, `id`, `Application_Name`, or `name` properties.
        # * The application ID as an integer.
        # * The application name as a string.
        $Application,

        [string]
        # The package number/name. If not provided, BuildMaster generates one based on the release settings.
        $PackageNumber,

        [hashtable]
        # Any package variables to set. Package variables are unique to each package.
        $Variable
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $parameters = @{ } 
    
    if( $PSCmdlet.ParameterSetName -eq 'ByReleaseID' )
    {
        $parameters | Add-BMObjectParameter -Name 'release' -Value $Release
    }
    else
    {
        $parameters['releaseNumber'] = $ReleaseNumber
        $parameters | Add-BMObjectParameter -Name 'application' -Value $Application
    }

    if( $PackageNumber )
    {
        $parameters['packageNumber'] = $PackageNumber
    }

    if( $Variable )
    {
        foreach( $key in $Variable.Keys )
        {
            $parameters[('${0}' -f $key)] = $Variable[$key]
        }
    }

    Invoke-BMRestMethod -Session $Session -Name 'releases/packages/create' -Parameter $parameters -Method Post
}


function New-BMPipeline
{
    <#
    .SYNOPSIS
    Creates a new pipeline in BuildMaster.
 
    .DESCRIPTION
    The `New-BMPipeline` function creates a new pipeline in BuildMaster and retuns an object representing the new pipeline. In order to deploy an application, you must create a release for that application. Each release gets assigned a pipeline, which are the set of steps to do when releasing and deploying. Pipelines can belong to a specific application or shared between applications.
 
    The pipeline is created with no stages. The following settings are enabled:
 
    * Enforce pipeline stage order for deployments
 
    The following settings are disabled:
 
    * Cancel earlier (lower-sequenced) releases that are still active and have not yet been deployed.
    * Create a new release by incrementing the final part after a release has been deployed.
    * Mark the release and package as deployed once it reaches the final stage.
 
    This function uses [BuildMaster's native API](http://inedo.com/support/documentation/buildmaster/reference/api/native).
 
    .EXAMPLE
    New-BMPipeline -Session $session -Name 'Powershell Module'
 
    Demonstrates how to create a new pipeline that is not used by any applications. In this example a pipeline named `PowerShell Module` will be created.
 
    .EXAMPLE
    New-BMPipeline -Session $session -Name 'PowerShell Module' -Application $app
 
    Demonstrates how to create a new pipeline and assign it to a specific application. In this example, the pipeline will be called `PowerShell Module` and it will be assigned to the `$app` application.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [object]
        # An object that represents the instance of BuildMaster to connect to. Use the `New-BMSession` function to creates a session object.
        $Session,

        [Parameter(Mandatory=$true)]
        [string]
        # The name of the pipeline.
        $Name,

        [object]
        # The application to assign the pipeline to. Can be:
        #
        # * An application object with `Application_Id`, `id`, `Application_Name`, or `name` properties.
        # * An application ID (must be an integer)
        # * An applicatoin name (must be a string)
        $Application,

        [string]
        # The background color BuildMaster should use when displaying the pipeline's name in the UI. Should be a CSS hexadecimal color, e.g. `#ffffff`
        $Color,

        [string[]]
        # Stage configuration for the pipeline. Should be an array of `<Inedo.BuildMaster.Pipelines.PipelineStage>` XML elements.
        $Stage
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $pipelineParams = @{
                        'Pipeline_Name' = $Name;
                        'Pipeline_Configuration' = @"
<Inedo.BuildMaster.Pipelines.Pipeline Assembly="BuildMaster">
  <Properties Name="Standard" Description="" EnforceStageSequence="True">
     <Stages>
        $( $Stage -join [Environment]::NewLine )
     </Stages>
     <PostDeploymentOptions>
        <Inedo.BuildMaster.Pipelines.PipelinePostDeploymentOptions Assembly="BuildMaster">
           <Properties CreateRelease="False" CancelReleases="False" DeployRelease="False" />
        </Inedo.BuildMaster.Pipelines.PipelinePostDeploymentOptions>
     </PostDeploymentOptions>
  </Properties>
</Inedo.BuildMaster.Pipelines.Pipeline>
"@
;
                        'Active_Indicator' = $true;
                   }
    if( $Application )
    {
        $pipelineParams | Add-BMObjectParameter -Name 'application' -Value $Application -ForNativeApi
    }

    if( $Color )
    {
        $pipelineParams['Pipeline_Color'] = $Color
    }

    $pipelineId = Invoke-BMNativeApiMethod -Session $session -Name 'Pipelines_CreatePipeline' -Parameter $pipelineParams -Method Post
    if( $pipelineId )
    {
        Invoke-BMNativeApiMethod -Session $session -Name 'Pipelines_GetPipeline' -Parameter @{ 'Pipeline_Id' = $pipelineId } -Method Post
    }
}


function New-BMRelease
{
    <#
    .SYNOPSIS
    Creates a new release for an application in BuildMaster.
 
    .DESCRIPTION
    The `New-BMRelease` function creates a release for an application in BuildMaster. It uses the BuildMaster [Release and Package Deployment API](http://inedo.com/support/documentation/buildmaster/reference/api/release-and-package).
 
    .EXAMPLE
    New-BMRelease -Session $session -Application 'BuildMasterAutomation' -Number '1.0' -Pipeline 'PowerShellModule'
 
    Demonstrates how to create a release using application/pipeline names. In this example, creates a `1.0` release for the `BuildMasterAutomation` application using the `PowerShellModule` pipeline.
 
    .EXAMPLE
    New-BMRelease -Session $session -Application 25 -Number '2.0' -Pipeline 3
 
    Demonstrates how to create a release using application/pipeline IDs. In this example, creates a `1.0` release for the application whose ID is `25` using the pipeline whose ID is `3`.
 
    .EXAMPLE
    New-BMRelease -Session $session -Application $app -Number '3.0' -Pipeline $pipeline
 
    Demonstrates how to create a release using application/pipeline objects. In this example, creates a `1.0` release for the application represented by the `$app` object (which must have either a `Application_Id` or `Application_Name` property that represent the ID and name of the application, respectively) using the pipeline represented by the `$pipeline` object (which must have either a `Pipeline_Id` or `Pipeline_Name` property that represent the ID and name of the pipeline, respectively).
 
    .EXAMPLE
    New-BMRelease -Session $session -Name 'BMA 1.0' -Application 'BuildMasterAutomation' -Number '1.0' -Pipeline 'PowerShellModule'
 
    Demonstrates how to create a release with a custom name. In this example, the release would be named `BMA 1.0`.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [object]
        # An object that represents what BuildMaster instance to connect to and what API key to use. Use `New-BMSession` to create a session object.
        $Session,

        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [object]
        # The application where the release should be created. Can be:
        #
        # * The application's name.
        # * The application's ID.
        # * An application object with either an `Application_Id` or `Application_Name` property that represent the application's ID and name, respectively.
        $Application,

        [Parameter(Mandatory=$true)]
        [string]
        # The release number, e.g. 1, 2, 3, 1.0, 2.0, etc.
        $Number,

        [Parameter(Mandatory=$true)]
        [object]
        # The pipeline the release should use. Can be:
        #
        # * The pipeline's name.
        # * The pipeline's ID.
        # * A pipeline object with either a `Pipeline_Id` or `Pipeline_Name` property that represent the pipeline's ID and name, respectively.
        $Pipeline,

        [string]
        # The name of the release. By default, BuildMaster uses the release number, passed with the `Number` parameter.
        $Name
    )

    process
    {
        Set-StrictMode -Version 'Latest'
        Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

        $parameters = @{
                            releaseNumber = $Number;
                            releaseName = $Name;
                       }
        
        $parameters | 
            Add-BMObjectParameter -Name 'application' -Value $Application -PassThru | 
            Add-BMObjectParameter -Name 'pipeline' -Value $Pipeline

        Invoke-BMRestMethod -Session $Session -Name 'releases/create' -Method Post -Parameter $parameters
    }
}



function New-BMServer
{
    <#
    .SYNOPSIS
    Creates a new server in a BuildMaster instance.
 
    .DESCRIPTION
    The `New-BMServer` function creates a new server in BuildMaster. Pass the name of the server to the `Name` parameter. Names may only contain letters, numbers, underscores, or dashes; they must begin with a letter; they must not end with dash or underscore. Pass the server type to the `Type` parameter. Type must be one of 'windows', 'powershell', 'ssh', or 'local'.
 
    Every server must have a unique name. If you create a server with a duplicate name, you'll get an error.
     
    This function uses BuildMaster's infrastructure management API.
 
    Pass a session object representing the instance of BuildMaster to use to the `Session` parameter. Use the `New-BMSession` function to create session objects.
 
    .LINK
    https://inedo.com/support/documentation/buildmaster/reference/api/infrastructure#data-specification
 
    .EXAMPLE
    New-BMServer -Session $session -Name 'example.com' -Windows
 
    Demonstrates how to create a new server that uses the Inedo Agent on Windows that doesn't encrypt the communication between the agent and the server.
 
    .EXAMPLE
    New-BMServer -Session $session -Name 'example.com' -Windows -EncryptionKey 'DEADBEEDEADBEEDEADBEEDEADBEEDEAD'
 
    Demonstrates how to create a new server that uses the Inedo Agent on Windows and uses an AES encryption key to encrypt the communication between the agent and server
 
    .EXAMPLE
    New-BMServer -Session $session -Name 'example.com' -Windows -Ssl -ForceSsl
 
    Demonstrates how to create a new server that uses the Inedo Agent on Windows and uses SSL to protect server to agent communications. As of BuildMaster 6.1.8, you *must* use the `ForceSsl` switch, otherwise SSL won't actually be enabled.
 
    .EXAMPLE
    New-BMServer -Session $session -Name 'example.com' -Ssh
 
    Demonstrates how to create a new server that uses SSH.
 
    .EXAMPLE
    New-BMServer -Session $session -Name 'example.com' -PowerShell
 
    Demonstrates how to create a new server that uses PowerShell Remoting.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory)]
        # An object representing the instance of BuildMaster to connect to. Use `New-BMSession` to create session objects.
        [object]$Session,

        [Parameter(Mandatory)]
        [ValidatePattern('^[A-Za-z][A-Za-z0-9_-]*(?<![_-])$')]
        [ValidateLength(1,50)]
        # The name of the server to create. Must contain only letters, numbers, underscores, or dashes. Must begin with a letter. Must not end with an underscore or dash. Must be between 1 and 50 characters long.
        [string]$Name,

        [Parameter(Mandatory,ParameterSetName='Local')]
        # Create a local server.
        [Switch]$Local,

        [Parameter(Mandatory,ParameterSetName='Windows')]
        [Parameter(Mandatory,ParameterSetName='WindowsAes')]
        [Parameter(Mandatory,ParameterSetName='WindowsSsl')]
        # Create a Windows server.
        [Switch]$Windows,

        [Parameter(Mandatory,ParameterSetName='Ssh')]
        # Create an SSH server.
        [Switch]$Ssh,

        [Parameter(ParameterSetName='Windows')]
        [Parameter(ParameterSetName='WindowsAes')]
        [Parameter(ParameterSetName='WindowsSsl')]
        [Parameter(ParameterSetName='Ssh')]
        # The server's host name. The default is to use the server's name.
        [string]$HostName,

        [Parameter(ParameterSetName='Windows')]
        [Parameter(ParameterSetName='WindowsAes')]
        [Parameter(ParameterSetName='WindowsSsl')]
        [Parameter(ParameterSetName='Ssh')]
        # The port to use. When adding a Windows server, the default is `46336`. When adding an SSH server, the default is `22`.
        [uint16]$Port,

        [Parameter(Mandatory,ParameterSetName='WindowsAes')]
        # The encryption key to use for the server. When passed, also automatically sets the server's encryption type to AES. Only used by Windows agents.
        [securestring]$EncryptionKey,

        [Parameter(Mandatory,ParameterSetName='WindowsSsl')]
        # Use SSL to communicate with the server's agent. Only used by Windows agents.
        [Switch]$Ssl,

        [Parameter(ParameterSetName='WindowsSsl')]
        # The server's agent only uses SSL. Only used by Windows agents.
        [Switch]$ForceSsl,

        [Parameter(Mandatory,ParameterSetName='PowerShell')]
        # Create a PowerShell server.
        [Switch]$PowerShell,

        [Parameter(ParameterSetName='PowerShell')]
        # The PowerShell remoting URL to use.
        [string]$WSManUrl,

        [Parameter(ParameterSetName='Ssh')]
        [Parameter(ParameterSetName='PowerShell')]
        # The name of the credential to use when connecting to the server via SSH or PowerShell Remoting.
        [string]$CredentialName,

        [Parameter(ParameterSetName='Ssh')]
        [Parameter(ParameterSetName='PowerShell')]
        # The temp path directory to use when connecting to the server via SSH or PowerShell Remoting. Default is `/tmp/buildmaster`.
        [string]$TempPath,

        # The environment(s) the server belongs in.
        [string[]]$Environment,

        # The server roles the server is part of.
        [string[]]$Role,

        # Any server-level variables to add to the server.
        [hashtable]$Variable,

        [Switch]
        # If set, creates the server but marks it as inactive.
        $Inactive
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $parameter = @{ 
                    'active' = (-not $InActive.IsPresent);
                 }

    if( -not $HostName )
    {
        $HostName = $Name
    }

    if( -not $TempPath )
    {
        $TempPath = '/tmp/buildmaster'
    }

    $serverType = $null
    if( $Windows )
    {
        if( -not $Port )
        {
            $Port = 46336
        }

        $serverType = 'windows'
        $parameter['hostName'] = $HostName
        $parameter['port'] = $Port

        if( $EncryptionKey )
        {
            $parameter['encryptionKey'] = (New-Object 'pscredential' 'encryptionkey',$EncryptionKey).GetNetworkCredential().Password
            $parameter['encryptionType'] = 'aes'
        }

        if( $Ssl )
        {
            $parameter['encryptionType'] = 'ssl'
            $parameter['requireSsl'] = $ForceSsl.IsPresent
        }
    }
    elseif( $Ssh )
    {
        if( -not $Port )
        {
            $Port = 22
        }

        $serverType = 'ssh'
        $parameter['hostName'] = $HostName
        $parameter['port'] = $Port
    }
    elseif( $PowerShell )
    {
        $serverType = 'powershell'

        if( $WSManUrl )
        {
            $parameter['wsManUrl'] = $WSManUrl
        }
    }
    elseif( $Local )
    {
        $serverType = 'local'
    }
    else
    {
        throw 'Don''t know how you got to this code. Well done!'
    }
    $parameter['serverType'] = $serverType;

    if( $Ssh -or $PowerShell )
    {
        if( $CredentialName )
        {
            $parameter['credentialsName'] = $CredentialName
        }

        if( $TempPath )
        {
            $parameter['tempPath'] = $TempPath
        }
    }

    if( $Environment )
    {
        $parameter['environments'] = $Environment
    }

    if( $Role )
    {
        $parameter['roles'] = $Role
    }

    if( $Variable )
    {
        $parameter['variables'] = $Variable
    }

    $encodedName = [uri]::EscapeDataString($Name)
    Invoke-BMRestMethod -Session $Session -Name ('infrastructure/servers/create/{0}' -f $encodedName) -Method Post -Parameter $parameter -AsJson
}


function New-BMServerRole
{
    <#
    .SYNOPSIS
    Creates a new server role in a BuildMaster instance.
 
    .DESCRIPTION
    The `New-BMServerRole` creates a new server role in BuildMaster. Pass the name of the role to the `Name` parameter. Names may only contain letters, numbers, spaces, periods, underscores, or dashes.
     
    Every role must have a unique name. If you create a role with a duplicate name, you'll get an error.
     
    This function uses BuildMaster's infrastructure management API.
 
    Pass a session object representing the instance of BuildMaster to use to the `Session` parameter. Use the `New-BMSession` function to create session objects.
 
    .EXAMPLE
    New-BMServerRole -Session $session -Name 'My Role'
 
    Demonstrates how to create a new server role.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory)]
        # An object representing the instance of BuildMaster to connect to. Use `New-BMSession` to create session objects.
        [object]$Session,

        [Parameter(Mandatory)]
        [ValidatePattern('^[A-Za-z0-9 ._-]+$')]
        # The name of the server role to create.
        [string]$Name
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $encodedName = [uri]::EscapeDataString($Name)
    Invoke-BMRestMethod -Session $Session -Name ('infrastructure/roles/create/{0}' -f $encodedName) -Method Post -Body '{}'
}


function New-BMSession
{
    <#
    .SYNOPSIS
    Creates a session object used to communicate with a BuildMaster instance.
 
    .DESCRIPTION
    The `New-BMSession` function creates and returns a session object that is required when calling any function in the BuildMasterAutomation module that communicates with BuildMaster. The session includes BuildMaster's URI and the credentials to use when making using BuildMaster's API.
 
    .EXAMPLE
    $session = New-BMSession -Uri 'https://buildmaster.com' -Credential $credential
 
    Demonstrates how to call `New-BMSession`. In this case, the returned session object can be passed to other BuildMasterAutomation module function to communicate with BuildMaster at `https://buildmaster.com` with the credential in `$credential`.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [uri]
        # The URI to the BuildMaster instance to use.
        $Uri,

        [Parameter(Mandatory=$true)]
        [string]
        # The API key to use when making requests to BuildMaster.
        $ApiKey
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    return [pscustomobject]@{
                                Uri = $Uri;
                                ApiKey = $ApiKey;
                            }
}


function Publish-BMReleasePackage
{
    <#
    .SYNOPSIS
    Deploys a release package in BuildMaster.
 
    .DESCRIPTION
    The `Publish-BMReleasePackage` deploys a release package in BuildMaster. The package is deployed using the pipeline assigned to the release the package is part of. This function uses BuildMaster's [Release and Package Deployment API](http://inedo.com/support/documentation/buildmaster/reference/api/release-and-package).
 
    Pass the package to deploy to the `Package` parameter. This can be a package object or a package ID (as an integer).
 
    To deploy a package, it must be part of a release that has a pipeline. That pipeline must have at least one stage and that stage must have a plan. If none of these conditions are met, you'll get no object back with no errors written.
 
    .EXAMPLE
    Publish-BMReleasePackage -Session $session -Package $package
 
    Demonstrates how to deploy a package by passing a package object to the `Package` parameter. This object must have an `id` or `pipeline_id` property.
     
    .EXAMPLE
    Publish-BMReleasePackage -Session $session -Package 383
 
    Demonstrates how to deploy a package by passing its ID to the `Package` parameter.
 
    .EXAMPLE
    Publish-BMReleasePackage -Session $session -Package $package -Stage $stage
 
    Demonstrates how to deploy a package to a specific stage of the release pipeline. By default, a package will deploy to the first stage of the assigned pipeline.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [object]
        # An object representing the BuildMaster instance to connect to. Use the `New-BMSession` function to create a session object.
        $Session,

        [Parameter(Mandatory=$true)]
        [object]
        # The package to deploy. Can be:
        #
        # * A package object which has an `id` property.
        # * The package's ID, as an integer.
        $Package,
        
        [string]
        # The name of the pipeline stage where the package will be deployed.
        $Stage,

        [Switch]
        # Instructs BuildMaster to run the deploy even if the deploy to previous stages failed.
        $Force
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $parameters = @{} | Add-BMObjectParameter -Name 'package' -Value $Package -PassThru
    
    if( $Stage )
    {
        $parameters['toStage'] = $Stage
    }

    if( $Force )
    {
        $parameters['force'] = 'true'
    }
    
    Invoke-BMRestMethod -Session $Session -Name 'releases/packages/deploy' -Parameter $parameters -Method Post
}



function Remove-BMServer
{
    <#
    .SYNOPSIS
    Removes a server from BuildMaster.
 
    .DESCRIPTION
    The `Remove-BMServer` function removes a server from BuildMaster. Pass the name of the server to remove to the `Name` parameter. If the server doesn't exist, nothing happens.
 
    Pass the session to the BuildMaster instance where you want to delete the server to the `Session` parameter. Use `New-BMSession` to create a session object.
 
    This function uses BuildMaster's infrastructure management API.
 
    .EXAMPLE
    Remove-BMServer -Session $session -Name 'example.com'
 
    Demonstrates how to delete a server.
 
    .EXAMPLE
    Get-BMServer -Session $session -Name 'example.com' | Remove-BMServer -Session $session
 
    Demonstrates that you can pipe the objects returned by `Get-BMServer` into `Remove-BMServer` to remove those servers.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory)]
        # The instance of BuildMaster to connect to.
        [object]$Session,

        [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
        # The name of the server to remove.
        [string]$Name
    )

    process
    {
        Set-StrictMode -Version 'Latest'
        Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

        $encodedName = [uri]::EscapeDataString($Name)
        Invoke-BMRestMethod -Session $Session -Name ('infrastructure/servers/delete/{0}' -f $encodedName) -Method Delete
    }
}


function Remove-BMServerRole
{
    <#
    .SYNOPSIS
    Removes a server role from BuildMaster.
 
    .DESCRIPTION
    The `Remove-BMServerRole` removes a server role from BuildMaster. Pass the name of the role to remove to the `Name` parameter. If the server role doesn't exist, nothing happens.
 
    Pass the session to the BuildMaster instance where you want to delete the role to the `Session` parameter. Use `New-BMSession` to create a session object.
 
    This function uses BuildMaster's infrastructure management API.
 
    .EXAMPLE
    Remove-BMServerRole -Session $session -Name 'Server Role'
 
    Demonstrates how to delete a server role.
 
    .EXAMPLE
    Get-BMServerRole -Session $session -Name 'My Role' | Remove-BMServerRole -Session $session
 
    Demonstrates that you can pipe the objects returned by `Get-BMServerRole` into `Remove-BMServerRole` to remove those roles.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory)]
        # An object representing the instance of BuildMaster to connect to. Use `New-BMSession` to create session objects.
        [object]$Session,

        [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
        # The name of the role to remove.
        [string]$Name
    )

    process
    {
        Set-StrictMode -Version 'Latest'
        Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

        $encodedName = [uri]::EscapeDataString($Name)
        Invoke-BMRestMethod -Session $Session -Name ('infrastructure/roles/delete/{0}' -f $encodedName) -Method Delete
    }
}


function Remove-BMVariable
{
    <#
    .SYNOPSIS
    Deletes BuildMaster variables.
 
    .DESCRIPTION
    The `Remove-BMVariable` function deletes BuildMaster variables. By default, it deletes global variables. It can also delete variables for a specific environment, server, server role, application group, and application variables.
 
    Pass the name of the variable to delete to the `Name` parameter. If no variable exists to delete, you'll get an error.
     
    To delete an environment's variables, pass the environment's name to the `EnvironmentName` parameter.
 
    To delete a server role's variables, pass the server role's name to the `ServerRoleName` parameter.
 
    To delete a server's variables, pass the server's name to the `ServerName` parameter.
 
    To delete an application group's variables, pass the application group's name to the `ApplicationGroupName` parameter.
 
    To delete an application's variables, pass the application's name to the `ApplicationName` parameter.
 
    Pass a session object representing the instance of BuildMaster to use to the `Session` parameter. Use `New-BMSession` to create a session object.
 
    This function uses BuildMaster's variables API. When deleting application and application group variables, it uses BuildMaster's native API.
 
    .EXAMPLE
    Remove-BMVariable -Session $session -Name 'Var'
 
    Demonstrates how to delete a global variable.
 
    .EXAMPLE
    Remove-BMVariable -Session $session -Name 'Var' -EnvironmentName 'Dev'
 
    Demonstrates how to delete a variable in an environment.
 
    .EXAMPLE
    Remove-BMVariable -Session $session -Name 'Var' -ServerRoleName 'WebApp'
 
    Demonstrates how to delete a variable in a server role.
 
    .EXAMPLE
    Remove-BMVariable -Session $session -Name 'Var' -ServerName 'example.com'
 
    Demonstrates how to delete a variable in a server.
 
    .EXAMPLE
    Remove-BMVariable -Session $session -Name 'Var' -ApplicationGroupName 'WebApps'
 
    Demonstrates how to delete a variable from an application group.
 
    .EXAMPLE
    Remove-BMVariable -Session $session -Name 'Var' -ApplicationName 'www'
 
    Demonstrates how to delete a variable from an application.
    #>

    [CmdletBinding(SupportsShouldProcess,DefaultParameterSetName='global')]
    param(
        [Parameter(Mandatory)]
        # An object representing the instance of BuildMaster to connect to. Use `New-BMSession` to create session objects.
        [object]$Session,

        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        # The name of the variable to delete.
        [string]$Name,

        [Parameter(Mandatory,ParameterSetName='application')]
        # The name of the application where the variable should be deleted. The default is to delete global variables.
        [string]$ApplicationName,

        [Parameter(Mandatory,ParameterSetName='application-group')]
        # The name of the application group where the variable should be deleted. The default is to delete global variables.
        [string]$ApplicationGroupName,

        [Parameter(Mandatory,ParameterSetName='environment')]
        # The name of the environment where the variable should be deleted. The default is to delete global variables.
        [string]$EnvironmentName,

        [Parameter(Mandatory,ParameterSetName='server')]
        # The name of the server where the variable should be deleted. The default is to delete global variables.
        [string]$ServerName,

        [Parameter(Mandatory,ParameterSetName='role')]
        # The name of the server role where the variable should be deleted. The default is to delete global variables.
        [string]$ServerRoleName
    )

    process
    {
        Set-StrictMode -Version 'Latest'
        Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

        $entityParamNames = @{
                                'application' = 'ApplicationName';
                                'application-group' = 'ApplicationGroupName';
                                'environment' = 'EnvironmentName';
                                'server' = 'ServerName';
                                'role' = 'ServerRoleName';
                            }

        $endpointName = 'variables/{0}' -f $PSCmdlet.ParameterSetName
        if( $PSCmdlet.ParameterSetName -ne 'global' )
        {
            $entityParamName = $entityParamNames[$PSCmdlet.ParameterSetName]
            $entityName = $PSBoundParameters[$entityParamName]
            $endpointName = '{0}/{1}' -f $endpointName,[uri]::EscapeDataString($entityName)
        }

        # Variables API doesn't delete application variables.
        if( $PSCmdlet.ParameterSetName -eq 'application' )
        {
            $app = Get-BMApplication -Session $session -Name $ApplicationName
            if( -not $app )
            {
                Write-Error -Message ('Application "{0}" does not exist.' -f $ApplicationName) -ErrorAction $ErrorActionPreference
                return
            }

            $variable = Invoke-BMNativeApiMethod -Session $session -Name 'Variables_GetVariablesForScope' -Method Post -Parameter @{ 'Application_Id' = $app.Application_Id }
            if( $variable )
            {
                Invoke-BMNativeApiMethod -Session $session -Name 'Variables_DeleteVariable' -Method Post -Parameter @{ 'Variable_Id' = $variable.Variable_Id }
            }
        }
        # Variables API doesn't delete application group variables.
        elseif( $PSCmdlet.ParameterSetName -eq 'application-group' )
        {
            $appGroup = Get-BMApplicationGroup -Session $session -Name $ApplicationGroupName
            if( -not $appGroup )
            {
                Write-Error -Message ('Application group "{0}" does not exist.' -f $ApplicationGroupName) -ErrorAction $ErrorActionPreference
                return
            }

            $variable = Invoke-BMNativeApiMethod -Session $session -Name 'Variables_GetVariablesForScope' -Method Post -Parameter @{ 'ApplicationGroup_Id' = $appGroup.ApplicationGroup_Id }
            if( $variable )
            {
                Invoke-BMNativeApiMethod -Session $session -Name 'Variables_DeleteVariable' -Method Post -Parameter @{ 'Variable_Id' = $variable.Variable_Id }
            }
        }
        else
        {
            $endpointName = '{0}/{1}' -f $endpointName,[uri]::EscapeDataString($Name)
            Invoke-BMRestMethod -Session $Session -Name $endpointName -Method Delete
        }
    }

}


function Set-BMRelease
{
    <#
    .SYNOPSIS
    Updates a release's pipeline or name.
 
    .DESCRIPTION
    The `Set-BMRelease` function updates a release's pipeline or name. To change a release's pipeline, pass the pipeline's ID to the `PipelineID` parameter. To change the pipeline's name, pass the new name to the `Name` parameter.
     
    This function uses the BuildMaster native API endpoint "Releases_CreateOrUpdateRelease".
 
    Pass the release you want to update to the `Release` parameter. You may pass the release's ID (as an integer), the release's number, or a release object as returned by the `Get-BMRelease` function.
 
    .EXAMPLE
    Set-BMRelease -Session $session -Release $release -PipelineID 45 -Name 'My New Name'
 
    Demonstrates how to update the pipeline and name of a release.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [object]
        # An object that represents what BuildMaster instance to connect to and what API key to use. Use `New-BMSession` to create a session object.
        $Session,

        [Parameter(Mandatory=$true,ParameterSetName='Update')]
        [object]
        # The release to update. Can be:
        #
        # * The release's number.
        # * The release's ID.
        # * An release object with either a `Release_Id` or `Release_Name` property that represent the application's ID and name, respectively.
        $Release,

        [int]
        # The ID of the release's new pipeline.
        $PipelineID,

        [string]
        # The new name of the release.
        $Name
    )

    process
    {
        Set-StrictMode -Version 'Latest'
        Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

        $bmRelease = Get-BMRelease -Session $Session -Release $Release
        if( -not $bmRelease )
        {
            Write-Error -Message ('Release "{0}" does not exist.' -f $Release)
            return
        }

        if( -not $PipelineID )
        {
            $PipelineID = $bmRelease.PipelineId
        }

        if( -not $Name )
        {
            $Name = $bmRelease.name
        }

        $parameter = @{ 
                        Application_Id = $bmRelease.ApplicationId;
                        Release_Number = $bmRelease.number;
                        Pipeline_Id = $PipelineID;
                        Release_Name = $Name;
                     }
        Invoke-BMNativeApiMethod -Session $Session -Name 'Releases_CreateOrUpdateRelease' -Method Post -Parameter $parameter | Out-Null

        Get-BMRelease -Session $Session -Release $bmRelease
    }
}


function Set-BMVariable
{
    <#
    .SYNOPSIS
    Create or set a BuildMaster variable.
 
    .DESCRIPTION
    The `Set-BMVariable` function creates or sets the value of a BuildMaster variable. By default, it creates/sets global variables. It can also set environment, server, server role, application group, and application variables.
 
    Pass the variable's name to the `Name` parameter. Pass the variable's value to the `Value` parameter. The value is passed as-is to BuildMaster.
     
    To set an environment's variable, pass the environment's name to the `EnvironmentName` parameter.
 
    To set a server role's variable, pass the server role's name to the `ServerRoleName` parameter.
 
    To set a server's variable, pass the server's name to the `ServerName` parameter.
 
    To set an application group's variable, pass the application group's name to the `ApplicationGroupName` parameter.
 
    To set an application's variable, pass the application's name to the `ApplicationName` parameter.
 
    Pass a session object representing the instance of BuildMaster to use to the `Session` parameter. Use `New-BMSession` to create a session object.
 
    This function uses BuildMaster's variables API.
 
    .EXAMPLE
    Set-BMVariable -Session $session -Name 'Var' -Value 'Value'
 
    Demonstrates how to create or set a global variable.
 
    .EXAMPLE
    Set-BMVariable -Session $session -Name 'Var' -Value 'Value' -EnvironmentName 'Dev'
 
    Demonstrates how to create or set a variable in an environment.
 
    .EXAMPLE
    Set-BMVariable -Session $session -Name 'Var' -Value 'Value' -ServerRoleName 'WebApp'
 
    Demonstrates how to create or set a variable for a server role.
 
    .EXAMPLE
    Set-BMVariable -Session $session -Name 'Var' -Value 'Value' -ServerName 'example.com'
 
    Demonstrates how to create or set a variable for a server.
 
    .EXAMPLE
    Set-BMVariable -Session $session -Name 'Var' -Value 'Value' -ApplicationGroupName 'WebApps'
 
    Demonstrates how to create or set a variable for an application group.
 
    .EXAMPLE
    Set-BMVariable -Session $session -Name 'Var' -Value 'Value' -ApplicationName 'www'
 
    Demonstrates how to create or set a variable for an application.
    #>

    [CmdletBinding(SupportsShouldProcess,DefaultParameterSetName='global')]
    param(
        [Parameter(Mandatory)]
        # An object representing the instance of BuildMaster to connect to. Use `New-BMSession` to create session objects.
        [object]$Session,

        [Parameter(Mandatory)]
        # The name of the variable to create.
        [string]$Name,

        [Parameter(Mandatory)]
        # The variable's value. Passed to BuildMaster as-is.
        [string]$Value,

        [Parameter(Mandatory,ParameterSetName='application')]
        # The name of the application where the variable should be created. The default is to create a global variable.
        [string]$ApplicationName,

        [Parameter(Mandatory,ParameterSetName='application-group')]
        # The name of the application group where the variable should be created. The default is to create a global variable.
        [string]$ApplicationGroupName,

        [Parameter(Mandatory,ParameterSetName='environment')]
        # The name of the environment where the variable should be created. The default is to create a global variable.
        [string]$EnvironmentName,

        [Parameter(Mandatory,ParameterSetName='server')]
        # The name of the server where the variable should be created. The default is to create a global variable.
        [string]$ServerName,

        [Parameter(Mandatory,ParameterSetName='role')]
        # The name of the server role where the variable should be created. The default is to create a global variable.
        [string]$ServerRoleName
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $entityParamNames = @{
                            'application' = 'ApplicationName';
                            'application-group' = 'ApplicationGroupName';
                            'environment' = 'EnvironmentName';
                            'server' = 'ServerName';
                            'role' = 'ServerRoleName';
                        }

    $endpointName = ('variables/{0}' -f $PSCmdlet.ParameterSetName)
    if( $PSCmdlet.ParameterSetName -ne 'global' )
    {
        $entityParamName = $entityParamNames[$PSCmdlet.ParameterSetName]
        $entityName = $PSBoundParameters[$entityParamName]
        $endpointName = '{0}/{1}' -f $endpointName,[uri]::EscapeDataString($entityName)
    }
    $endpointName = '{0}/{1}' -f $endpointName,[uri]::EscapeDataString($Name)

    Invoke-BMRestMethod -Session $Session -Name $endpointName -Method Post -Body $Value
}

 function Stop-BMRelease 
{
    <#
    .SYNOPSIS
    Cancels a release.
 
    .DESCRIPTION
    The `Stop-BMRelease` function cancels a BuildMaster release. It calls the `Releases_CancelRelease` native API endpoint.
 
    .EXAMPLE
    Stop-BMRelease -Session $session -Application 11 -Number 1.1
 
    Demonstrates how to cancel a release. In this case, the `1.1` release of the application whose ID is `11` is cancelled.
 
    .EXAMPLE
    Stop-BMRelease -Session $session -Application 'BuildMaster Automation' -Number 1.1
 
    Demonstrates how to cancel a release. In this case, the `1.1` release of the `BuildMaster Automation` application is cancelled.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [object]
        # The session to use when connecting to BuildMaster. Use `New-BMSession` to create session objects.
        $Session,

        [Parameter(Mandatory=$true)]
        [int]
        # The ID of the application whose release should be cancelled.
        $ApplicationID,

        [Parameter(Mandatory=$true)]
        [string]
        # The release number, e.g. 1, 2, 3, 1.0, 2.0, etc.
        $Number,

        [string]
        # The reason for cancelling the release.
        $Reason
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $parameter = @{
                    Application_Id = $ApplicationID;
                    Release_Number = $Number;
                    CancelledReason_Text = $Reason
                }

    Invoke-BMNativeApiMethod -Session $Session -Name 'Releases_CancelRelease' -Parameter $parameter -Method Post
}

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

function Use-CallerPreference
{
    <#
    .SYNOPSIS
    Sets the PowerShell preference variables in a module's function based on the callers preferences.
 
    .DESCRIPTION
    Script module functions do not automatically inherit their caller's variables, including preferences set by common parameters. This means if you call a script with switches like `-Verbose` or `-WhatIf`, those that parameter don't get passed into any function that belongs to a module.
 
    When used in a module function, `Use-CallerPreference` will grab the value of these common parameters used by the function's caller:
 
     * ErrorAction
     * Debug
     * Confirm
     * InformationAction
     * Verbose
     * WarningAction
     * WhatIf
     
    This function should be used in a module's function to grab the caller's preference variables so the caller doesn't have to explicitly pass common parameters to the module function.
 
    This function is adapted from the [`Get-CallerPreference` function written by David Wyatt](https://gallery.technet.microsoft.com/scriptcenter/Inherit-Preference-82343b9d).
 
    There is currently a [bug in PowerShell](https://connect.microsoft.com/PowerShell/Feedback/Details/763621) that causes an error when `ErrorAction` is implicitly set to `Ignore`. If you use this function, you'll need to add explicit `-ErrorAction $ErrorActionPreference` to every function/cmdlet call in your function. Please vote up this issue so it can get fixed.
 
    .LINK
    about_Preference_Variables
 
    .LINK
    about_CommonParameters
 
    .LINK
    https://gallery.technet.microsoft.com/scriptcenter/Inherit-Preference-82343b9d
 
    .LINK
    http://powershell.org/wp/2014/01/13/getting-your-script-module-functions-to-inherit-preference-variables-from-the-caller/
 
    .EXAMPLE
    Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
 
    Demonstrates how to set the caller's common parameter preference variables in a module function.
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        #[Management.Automation.PSScriptCmdlet]
        # The module function's `$PSCmdlet` object. Requires the function be decorated with the `[CmdletBinding()]` attribute.
        $Cmdlet,

        [Parameter(Mandatory = $true)]
        [Management.Automation.SessionState]
        # The module function's `$ExecutionContext.SessionState` object. Requires the function be decorated with the `[CmdletBinding()]` attribute.
        #
        # Used to set variables in its callers' scope, even if that caller is in a different script module.
        $SessionState
    )

    Set-StrictMode -Version 'Latest'

    # List of preference variables taken from the about_Preference_Variables and their common parameter name (taken from about_CommonParameters).
    $commonPreferences = @{
                              'ErrorActionPreference' = 'ErrorAction';
                              'DebugPreference' = 'Debug';
                              'ConfirmPreference' = 'Confirm';
                              'InformationPreference' = 'InformationAction';
                              'VerbosePreference' = 'Verbose';
                              'WarningPreference' = 'WarningAction';
                              'WhatIfPreference' = 'WhatIf';
                          }

    foreach( $prefName in $commonPreferences.Keys )
    {
        $parameterName = $commonPreferences[$prefName]

        # Don't do anything if the parameter was passed in.
        if( $Cmdlet.MyInvocation.BoundParameters.ContainsKey($parameterName) )
        {
            continue
        }

        $variable = $Cmdlet.SessionState.PSVariable.Get($prefName)
        # Don't do anything if caller didn't use a common parameter.
        if( -not $variable )
        {
            continue
        }

        if( $SessionState -eq $ExecutionContext.SessionState )
        {
            Set-Variable -Scope 1 -Name $variable.Name -Value $variable.Value -Force -Confirm:$false -WhatIf:$false
        }
        else
        {
            $SessionState.PSVariable.Set($variable.Name, $variable.Value)
        }
    }

}