Public/Get-StagecoachInventory.ps1

#Requires -Version 7.0
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

function Get-StagecoachInventory {
    <#
    .SYNOPSIS
        Discovers Azure VMs and Arc-enabled servers across subscriptions via Azure Resource Graph.
    .DESCRIPTION
        Queries Azure Resource Graph for all virtual machines and hybrid compute instances,
        parsing their OS, power state, and domain/workgroup status.
    .PARAMETER SubscriptionId
        Optional Subscription ID to filter results.
    .OUTPUTS
        StagecoachTarget[]
    #>

    [CmdletBinding()]
    [OutputType([StagecoachTarget[]])]
    param(
        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)]
        [string]$SubscriptionId
    )

    process {
        $kqlQuery = @"
Resources
| where type =~ 'microsoft.compute/virtualmachines'
    or type =~ 'microsoft.hybridcompute/machines'
| extend kind = iff(type =~ 'microsoft.compute/virtualmachines', 'AzureVM', 'ArcServer'),
         osType = coalesce(tostring(properties.storageProfile.osDisk.osType), tostring(properties.osType), tostring(properties.osName)),
         osName = coalesce(tostring(properties.osName), tostring(properties.osProfile.computerName), name),
         powerState = coalesce(tostring(properties.extended.instanceView.powerState.displayStatus), tostring(properties.status)),
         agentStatus = tostring(properties.status),
         domainName = coalesce(tostring(properties.domainName), '')
| project id, name, resourceGroup, subscriptionId, tenantId, location, kind, osType, osName, powerState, agentStatus, domainName, tags
"@


        $results = Invoke-ArgQuery -Query $kqlQuery -SubscriptionId $SubscriptionId

        $targets = [System.Collections.Generic.List[StagecoachTarget]]::new()

        foreach ($item in $results) {
            $target = [StagecoachTarget]::new()
            $target.Id = $item.id
            $target.Name = $item.name
            $target.ResourceGroup = $item.resourceGroup
            $target.SubscriptionId = $item.subscriptionId
            $target.TenantId = $item.tenantId
            $target.Location = $item.location
            $target.Kind = [StagecoachTargetKind]::$($item.kind)
            $target.OsType = $item.osType
            $target.OsName = $item.osName
            $target.PowerState = $item.powerState
            $target.AgentStatus = $item.agentStatus
            $target.DomainName = $item.domainName

            # Domain classification logic
            if ([string]::IsNullOrWhiteSpace($item.domainName) -or $item.domainName -eq 'WORKGROUP' -or $item.domainName -eq $item.name) {
                $target.DomainType = [StagecoachDomainType]::Workgroup
            }
            else {
                $target.DomainType = [StagecoachDomainType]::ActiveDirectory
            }

            if ($item.tags) {
                $tagsObj = $item.tags
                if ($tagsObj -is [PSCustomObject]) {
                    foreach ($prop in $tagsObj.PSObject.Properties) {
                        $target.Tags[$prop.Name] = [string]$prop.Value
                    }
                }
            }

            $targets.Add($target)
        }

        return , $targets.ToArray()
    }
}