Modules/Private/Processing/Start-AZTIExtraJobs.ps1

<#
.Synopsis
Run the non-collector processing work (security, policy, advisory, subscriptions, diagram).

.DESCRIPTION
Runs the security-centre, policy, advisory and subscription processing in-process and returns
their results, then starts the draw.io diagram work. Formerly this started one background job
per item; see the AB#5649 notes on the function for why that changed.

.Link
https://github.com/thisismydemo/azure-scout/Modules/Private/2.ProcessingFunctions/Start-AZSCExtraJobs.ps1

.COMPONENT
This PowerShell Module is part of Azure Scout (AZSC).

.NOTES
Version: 3.6.0
First Release Date: 15th Oct, 2024
Authors: Claudio Merola
#>


function Start-AZSCExtraJobs {
    Param ($SkipDiagram,
            $SkipAdvisory,
            $SkipPolicy,
            $SecurityCenter,
            $Subscriptions,
            $Resources,
            $Advisories,
            $DDFile,
            $DiagramCache,
            $FullEnv,
            $ResourceContainers,
            $Security,
            $PolicyDef,
            $PolicySetDef,
            $PolicyAssign,
            $Automation,
            $IncludeCosts,
            $CostData)
    # ── StrictMode boundary ──────────────────────────────────────────────────────────
    # This is the v1 inventory engine, forked from microsoft/ARI, written without StrictMode
    # and carrying property reads over API payloads whose shape varies by tenant. StrictMode is
    # dynamically scoped, so turning it off here covers this call tree only; the assessment
    # platform under src/ keeps it in full force. Turning it on here is AB#5667's job, together
    # with the recorded live-payload fixtures needed to do it safely.
    Set-StrictMode -Off

    <#
        AB#5649 — these four ran as background jobs and now run in-process.

        Each was a wrapper whose entire body was "Start-Job { import-module <this module>;
        Start-AZSC<X>Job ... }". That bought no parallelism worth having and cost three things:

        1. THE SAME NotStarted RACE AS AB#5629, still live in v2.5.3. The harvest side in
           Start-AZSCExtraReports waited with
               while (get-job -Name 'Policy' | Where-Object { $_.State -eq 'Running' })
           which does not match a job that has not started yet. Start-Job is asynchronous, so
           that wait could fall straight through, Receive-Job would return nothing and
           Remove-Job would destroy the job — the Policy, Advisory, Security and Subscriptions
           sheets could each silently come back empty. The resource pipeline's copy of this bug
           was fixed; these four copies were missed.

        2. THE STRICTMODE LEAK. `import-module` inside the job re-imported this module, so
           module-scope StrictMode applied again inside the job even when the caller had opted
           out. That is why the v2.5.3 opt-out had to be repeated at 17 entry points rather
           than 5 — every one of these job bodies was one of them.

        3. SILENTLY WRONG ARGUMENTS. Invoke-AZSCSecurityCenterJob was called with
           `-SecurityCenter $SecurityCenter`, but its parameter block declared no such
           parameter. PowerShell does not reject an unknown named argument to a simple
           function — it collects it into $args and carries on — so $SecurityCenter was
           undefined inside the wrapper, $null crossed the job boundary as the -Security
           argument, and Start-AZSCSecCenterJob's `foreach ($1 in $Security)` iterated nothing.
           The Security Center sheet has been empty in every release that had one. Calling the
           function directly is what surfaced it, and it is fixed below by passing $Security.

        The four wrapper files (Invoke-AZTI{Advisory,Policy,SecurityCenter,Sub}Job.ps1) are
        deleted; there is nothing left for them to wrap. The draw.io diagram work still runs as
        a job and keeps its wrapper — that subsystem starts nested jobs of its own and is a
        separate piece of work.

        Returns a hashtable the reporting phase consumes instead of calling Receive-Job.
    #>


    $Results = @{
        Security      = $null
        Policy        = $null
        Advisory      = $null
        Subscriptions = $null
    }

    <######################################################### DRAW IO DIAGRAM JOB ######################################################################>

    # Resolve the full module path so the diagram job can import it even when not in PSModulePath
    $LoadedModule = Get-Module -Name AzureScout
    if ($LoadedModule) {
        $AZSCModule = $LoadedModule.Path
    } else {
        $AZSCModule = 'AzureScout'
    }

    Write-Debug ((get-date -Format 'yyyy-MM-dd_HH_mm_ss')+' - '+'Checking if Draw.io Diagram Job Should be Run.')
    if (![bool]$SkipDiagram) {
        Write-Debug ((get-date -Format 'yyyy-MM-dd_HH_mm_ss')+' - '+'Starting Draw.io Diagram Processing Job.')
        Invoke-AZSCDrawIOJob -Subscriptions $Subscriptions -Resources $Resources -Advisories $Advisories -DDFile $DDFile -DiagramCache $DiagramCache -FullEnv $FullEnv -ResourceContainers $ResourceContainers -Automation $Automation -AZSCModule $AZSCModule
    }

    <######################################################### SECURITY CENTER ######################################################################>

    Write-Debug ((get-date -Format 'yyyy-MM-dd_HH_mm_ss')+' - '+'Checking If Should Process Security Center.')
    if (![string]::IsNullOrEmpty($SecurityCenter))
        {
            Write-Debug ((get-date -Format 'yyyy-MM-dd_HH_mm_ss')+' - '+'Processing Security Center.')
            try {
                # $Security, NOT $SecurityCenter -- see note 3 above. $SecurityCenter is the
                # switch that says whether to do this at all; $Security carries the rows.
                $Results.Security = Start-AZSCSecCenterJob -Subscriptions $Subscriptions -Security $Security
            }
            catch {
                Write-Warning "[AzureScout] Security Center processing failed and was skipped: $($_.Exception.Message)"
            }
        }

    <######################################################### POLICY ######################################################################>

    Write-Debug ((get-date -Format 'yyyy-MM-dd_HH_mm_ss')+' - '+'Checking If Should Process Policy.')
    if (![bool]$SkipPolicy) {
        if (![string]::IsNullOrEmpty($PolicyAssign) -and ![string]::IsNullOrEmpty($PolicyDef))
            {
                Write-Debug ((get-date -Format 'yyyy-MM-dd_HH_mm_ss')+' - '+'Processing Policy.')
                try {
                    $Results.Policy = Start-AZSCPolicyJob -Subscriptions $Subscriptions -PolicySetDef $PolicySetDef -PolicyAssign $PolicyAssign -PolicyDef $PolicyDef
                }
                catch {
                    Write-Warning "[AzureScout] Policy processing failed and was skipped: $($_.Exception.Message)"
                }
            }
    }

    <######################################################### ADVISORY ######################################################################>

    Write-Debug ((get-date -Format 'yyyy-MM-dd_HH_mm_ss')+' - '+'Checking If Should Process Advisory.')
    if (![bool]$SkipAdvisory) {
        if (![string]::IsNullOrEmpty($Advisories))
            {
                Write-Debug ((get-date -Format 'yyyy-MM-dd_HH_mm_ss')+' - '+'Processing Advisory.')
                try {
                    $Results.Advisory = Start-AZSCAdvisoryJob -Advisories $Advisories
                }
                catch {
                    Write-Warning "[AzureScout] Advisory processing failed and was skipped: $($_.Exception.Message)"
                }
            }
    }

    <######################################################### SUBSCRIPTIONS ######################################################################>

    Write-Debug ((get-date -Format 'yyyy-MM-dd_HH_mm_ss')+' - '+'Processing Subscriptions.')
    try {
        $Results.Subscriptions = Start-AZSCSubscriptionJob -Subscriptions $Subscriptions -Resources $Resources -CostData $CostData
    }
    catch {
        Write-Warning "[AzureScout] Subscription processing failed and was skipped: $($_.Exception.Message)"
    }

    $Results
}