lib/Brokers.ps1


function New-TMBroker {
    <#
    .SYNOPSIS
    Creates a new TMBroker class object
 
    .DESCRIPTION
    This function
 
    .PARAMETER TMSession
    The name of the TransitionManager sesssion to use with the new Broker
 
    .PARAMETER TMEvent
    A TMEvent class object representing the TransitionManager Event in which the Broker will be operating
 
    .PARAMETER TaskId
    The Id of the Broker task
 
    .PARAMETER SubjectScopeType
    The type of scoping the Broker will use to determine its subject Tasks. The Inline scope type will
    determine
 
    .PARAMETER SubjectScopeTaskProperty
    A property on the Tasks that will be evaluated to determine the Broker's subject Tasks
 
    .PARAMETER SubjectScopeMatchingCriteria
    The value(s) to match on the SubjectScopeTaskProperty
 
    .PARAMETER SubjectScopeMatchExpression
    A ScriptBlock that will be used to filter the Tasks to determine the Broker's subject Tasks
 
    .PARAMETER SubjectScopeTaskFilter
    A TMBrokerTaskFilter object that will be used in the TMQL query to filter the Tasks to determine the Broker's subject Tasks
 
    .PARAMETER TimeoutMinutes
    The amount of time in minutes that the Broker can run before automatically terminating its execution
 
    .PARAMETER PauseSeconds
    The amount of time in seconds that the Broker should pause in between processing subject tasks
 
    .PARAMETER Parallel
    Switch: This switch will have the broker run multiple parallel runspaces for subjects it encounters.
 
    .PARAMETER Throttle
    Required when Parallel is used. This Parameter controls how many concurrent threads the Broker will be allowed to use
 
    .EXAMPLE
    # Login to TransitionManager
    $SessionSplat = @{
        SessionName = 'Broker'
        Server = ([URI]$ActionRequest.options.callback.siteUrl).host
        Credential = (Get-StoredCredential TM)
        Project = $ActionRequest.task.project.name
    }
    $Session = New-TMSession @SessionSplat
 
    # Get the Event
    $TMEvent = Get-TMEvent -TMSession 'Broker' -Name $ActionRequest.task.event.name -ProjectId $ActionRequest.options.projectId
 
    # Create a new Broker
    $Broker = New-TMBroker -TMSession 'Broker' -TMEvent $TMEvent -TaskId $ActionRequest.task.id
 
    .EXAMPLE
    # Create a new Broker that scopes all Tasks with a Category of 'AWS MGN' or 'VMware vCenter'
    $BrokerSplat = @{
        SubjectScopeType = 'Service'
        SubjectScopeTaskProperty = 'Category
        SubjectScopeMatchingCriteria = 'AWS MGN', 'VMware vCenter'
        Parallel = $true
    }
    $Broker = New-TMBroker -TaskId $ActionRequest.task.id
 
    .EXAMPLE
    # Create a new Broker that scopes all Tasks whose Action names match either 'vCenter' or 'MGN' and
    # have an associated 'VM' type Asset
    $BrokerSplat = @{
        SubjectScopeType = 'Service'
        SubjectScopeMatchExpression = {($_.Action.Name -match '(vCenter)|(MGN)) -and ($_.Asset.Type -eq 'VM')}
        Parallel = $true
    }
    $Broker = New-TMBroker -TaskId $TM.Task.Id
 
    .OUTPUTS
    A TMBroker class object
    #>


    [CmdletBinding(DefaultParameterSetName = 'ByTaskProperty')]
    param (
        [Parameter(Mandatory = $false, Position = 0)]
        [String]$TMSession = 'Default',

        [Parameter(Mandatory = $false, Position = 1)]
        [TMEvent]$TMEvent,

        [Parameter(Mandatory = $false, Position = 2)]
        [Int]$TaskId,

        [Parameter(Mandatory = $false)]
        [ValidateSet('Service', 'Inline')]
        [Alias('Type')]
        [String]$SubjectScopeType = 'Service',

        [Parameter(Mandatory = $false, ParameterSetName = 'ByTaskProperty')]
        [ValidateSet('Title', 'Category', 'Team', 'Action')]
        [Alias('TaskProperty')]
        [String]$SubjectScopeTaskProperty = 'Title',

        [Parameter(Mandatory = $false, ParameterSetName = 'ByTaskProperty')]
        [Alias('MatchingCriteria')]
        [String[]]$SubjectScopeMatchingCriteria,

        [Parameter(Mandatory = $true, ParameterSetName = 'ByExpression')]
        [Alias('MatchExpression')]
        [ScriptBlock]$SubjectScopeMatchExpression,

        [Parameter(Mandatory = $true, ParameterSetName = 'ByFilter')]
        [Alias('MatchFilter', 'Filter')]
        [TMBrokerTaskFilter]$SubjectScopeTaskFilter,

        [Parameter(Mandatory = $false)]
        [Int]$TimeoutMinutes = 120,

        [Parameter(Mandatory = $false)]
        [Int]$PauseSeconds = 15,

        [Parameter()]
        [Switch]$Parallel,

        [Parameter(Mandatory = $false)]
        [Int]$Throttle = 8
    )


    # Get the session configuration
    Write-Verbose 'Checking for cached TMSession'
    $TMSessionConfig = $global:TMSessions[$TMSession]
    Write-Debug 'TMSessionConfig:'
    Write-Debug ($TMSessionConfig | ConvertTo-Json -Depth 5)
    if (-not $TMSessionConfig) {
        throw "TMSession '$TMSession' not found. Use New-TMSession command before using features."
    }

    # Initialize a new broker
    if ($PSCmdlet.ParameterSetName -eq 'ByTaskProperty') {
        $Broker = [TMBroker]::new(
            $SubjectScopeType,
            $SubjectScopeTaskProperty,
            $SubjectScopeMatchingCriteria,
            $TimeoutMinutes,
            $PauseSeconds,
            $Parallel.IsPresent,
            $Throttle
        )
    } elseif ($PSCmdlet.ParameterSetName -eq 'ByFilter') {
        $Broker = [TMBroker]::new(
            $SubjectScopeType,
            $SubjectScopeTaskFilter,
            $TimeoutMinutes,
            $PauseSeconds,
            $Parallel.IsPresent,
            $Throttle
        )
    } else {
        $Broker = [TMBroker]::new(
            $SubjectScopeType,
            $SubjectScopeMatchExpression,
            $TimeoutMinutes,
            $PauseSeconds,
            $Parallel.IsPresent,
            $Throttle
        )
    }

    # Load the TM Session into the broker
    $Broker.TMSession = $TMSessionConfig

    ## Add the TM Event supplied by the invocation
    if ($TMEvent) {
        $Broker.TMSession.UserContext.event = @{
            Id   = $TMEvent.id
            Name = $TMEvent.Name
        }
    } elseif (
        [String]::IsNullOrWhiteSpace($Broker.TMSession.UserContext.event.Id) -or
        [String]::IsNullOrWhiteSpace($Broker.TMSession.UserContext.event.Name)
    ) {
        throw "TMSession $($TMSession.Name) does not contain an Event Name or Id in the UserContext property. Provide a value for the -TMEvent parameter"
    }

    # Load the Event's data into the broker
    $Broker.GetEventData()

    # Fill the task data if a Task Id was provided
    if ($TaskId) {
        $Broker.GetTaskData($TaskId)
    }

    # Return the new broker
    $Broker
}


function Get-TMBrokerTaskData {
    <#
    .SYNOPSIS
    Gets information about the Tasks that will be managed by the Broker
 
    .DESCRIPTION
    This function will load the following information into the provided Broker object:
        - The Broker's Task information
        - The Init Task (if present)
        - Information on each of the subject tasks that are to be managed by the Broker
 
    .PARAMETER Broker
    A TMBroker object that has been loaded with TMSession and TMEvent data
 
    .PARAMETER TaskId
    The Id of the Broker task
 
    .EXAMPLE
    Get-TMBrokerTaskData -Broker $Broker -TaskId $ActionRequest.task.id
 
    .OUTPUTS
    None
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
        [TMBroker]$Broker,

        [Parameter(Mandatory = $true, Position = 1)]
        [Int]$TaskId
    )

    if (-not $Broker.EventData) {
        throw "The Broker has not been loaded with Event data. Use either the New-TMBroker command or the Broker's GetEventData() method"
    }

    $Broker.GetTaskData($TaskId)
}


function Get-TMBrokerCache {
    <#
    .SYNOPSIS
    Loads the Broker with a data cache by executing the Init Task
 
    .DESCRIPTION
    This function will populate the Broker's data cache by executing the Init Task (if present)
 
    .PARAMETER Broker
    The TMBroker object that will be updated with cache data
 
    .EXAMPLE
    # Load the cache if there's an init task
    if ($Broker.Init) {
        Get-TMBrokerCache -Broker $Broker
    }
 
    .OUTPUTS
    None
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
        [TMBroker]$Broker
    )


    begin {

    }

    process {
        if (-not $Broker.TMSession) {
            throw "The Broker has not been loaded with a TM Session. Use either the New-TMBroker command or load the Broker's TMSession property"
        }

        if (-not $Broker.EventData) {
            throw "The Broker has not been loaded with Event data. Use either the New-TMBroker command or the Broker's GetEventData() method"
        }

        if (-not $Broker.Task -or -not $Broker.Subjects) {
            throw "The Broker has not been loaded with Task data. Use either the Get-TMBrokerTaskData command or the Broker's GetTaskData() method"
        }

        if (-not $Broker.Init) {
            throw "The Broker has not been loaded with an Init Task. Use either the Get-TMBrokerTaskData command or the Broker's GetInitTask() method"
        }

        ## Run the Init Task
        $Broker.PopulateCache()
    }
}


function Start-TMBroker {
    <#
    .SYNOPSIS
    Runs the Broker to execute the subject tasks in its scope
 
    .DESCRIPTION
    This function will execute the Broker. This will, in turn, execute all of the Subject Tasks in its scope.
 
    .PARAMETER Broker
    The TMBroker object that will be updated with cache data
 
    .EXAMPLE
    # Start the Broker
    Start-TMBroker -Broker $Broker
 
    .OUTPUTS
    None
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
        [TMBroker]$Broker
    )


    begin {

    }

    process {
        if (-not $Broker.TMSession) {
            throw "The Broker has not been loaded with a TM Session. Use either the New-TMBroker command or load the Broker's TMSession property"
        }

        if (-not $Broker.EventData) {
            throw "The Broker has not been loaded with Event data. Use either the New-TMBroker command or the Broker's GetEventData() method"
        }

        if (-not $Broker.Task) {
            throw 'The Broker does not have a task assigned.'
        }
        if (-not $Broker.Subjects -and -not $Broker.ServiceSubjects) {
            throw "The Broker has not been loaded with Task data. Use either the Get-TMBrokerTaskData command or the Broker's GetTaskData() method"
        }

        ## Run the Broker
        $Broker.Run()
    }
}