Private/Sessions.ps1

<#
.SYNOPSIS
Registers a session.
 
.DESCRIPTION
Registers a session by determining Resource Provider specific information.
Utilizes the information gathered, validated, and stored in $Global:session.
The information gathered is determined by the resource type, however some resource information is general.
We do not do a lot of input validation at this point since the inputs should already have been validated in prior steps.
 
.NOTES
Initial framework author: Cale Vernon (CAVERNON)
Adapted from the works of: Hayder Sabeeh (HAALZUBA); Qing Liu (QLIU); Andy Zhao (KUZHAO)
#>


function Register-Session
{
    # Begin parsing the URI.
    # This information is used as the foundation for later finding the Resource Provider specific instance information.
    $tokens = ($Global:session.uri).Split('/')
    $sessionProperties = @{
        subscriptionID = $tokens[2]
        resourceGroup = $tokens[4]
        resourceProvider = $tokens[6]
        resourceType = $tokens[7]
        # Spaces are trimmed on the resource token as URIs copied from other tools, like ASC, tend to have a trailing space.
        resource = ($tokens[8] -Replace '\s', '')
        timespanStart = (Get-Date($Global:session.start)).ToString()
        timespanEnd = (Get-Date($Global:session.end)).ToString()
        # If the follow Kusto and Jarvis time calculations are changed then you may need to update the Invoke-Operation functions in order to be reflected in Free Mode.
        timespanStartKusto = "$(Get-Date $Global:session.start -Format s)Z"
        timespanEndKusto = "$(Get-Date $Global:session.end -Format s)Z"
        timespanStartJarvisGlobal = ((New-TimeSpan -Start (Get-Date('2020-01-01')) -End (Get-Date($Global:session.start)).ToString("yyyy'-'MM'-'dd")).TotalSeconds * 1000 + 1577836800000).ToString()
        timespanEndJarvisGlobal = ((New-TimeSpan -Start (Get-Date('2020-01-01')) -End (Get-Date($Global:session.end)).ToString("yyyy'-'MM'-'dd")).TotalSeconds * 1000 + 1577836800000).ToString()
    }
    $Global:session | Add-Member -NotePropertyMembers $sessionProperties

    # Begin using actual resource location processes.
    Switch ($Global:session.resourceType) {
        <########################################
        # Start: VM instance selection.
        #########################################>

        'virtualMachines' {
            # Locate instances of the resource.
            Write-Console "Searching for instances of $($Global:session.resource)..."
            $query = "
                cluster('AzureCM').database('AzureCM').LogContainerSnapshot
                | where PreciseTimeStamp >= datetime($($Global:session.timespanStartKusto)) and PreciseTimeStamp <= datetime($($Global:session.timespanEndKusto))
                | where subscriptionId in ('$($Global:session.subscriptionID)')
                | where roleInstanceName in ('_$($Global:session.resource)', '$($Global:session.resource)')
                | summarize ContainerChangeDateOrEarliestKnownDate=min(PreciseTimeStamp) by CloudName, Region, DataCenterName, Tenant, AvailabilityZone, nodeId, containerId, roleInstanceName, tenantName, virtualMachineUniqueId
                | join kind=leftouter(cluster('Azuredcm'). database('AzureDCMDb').ResourceSnapshotV1) on `$left.nodeId == `$right.ResourceId
                | extend NodeIdUpper=toupper(nodeId)
                | join kind=leftouter(cluster('aznwcc').database('aznwmds').Servers) on `$left.NodeIdUpper == `$right.NodeId
                | join kind=leftouter(cluster('aznwcc').database('aznwmds').DeviceInterfaceLinks) on `$left.DeviceName == `$right.StartDevice
                | project roleInstanceName, ContainerChangeDateOrEarliestKnownDate, Region, DataCenterName, Tenant, AvailabilityZone,virtualMachineUniqueId, nodeId, containerId, tenantName, IPAddress, EndDevice
                | order by ContainerChangeDateOrEarliestKnownDate desc
            "

            $instances = Invoke-Kusto -KustoCluster 'AzureCM' -KustoDatabase 'AzureCM' -Query $query
            # Instances were found; begin instance selection and session creation.
            If ($instances) {
                $instance = $instances | Out-GridView -Title "[Nanite] Please select an instance of $($Global:session.resource):" -OutputMode Single
                If (!$instance) {
                    Write-Console "No instances of $($Global:session.resource) were selected."
                    Return
                }
                Else {
                    # Look up the region's Shoebox name to be used in certain operations such as VMDash.
                    $query = "
                        cluster('AzureCM').database('AzureCM').LogClusterSnapshot
                        | where tenantName contains '$($instance.Tenant)'
                        | where shoeboxMdmAccountName !in ('', 'NA')
                        | distinct shoeboxMdmAccountName
                    "

                    $regionShoebox = Invoke-Kusto -KustoCluster 'AzureCM' -KustoDatabase 'AzureCM' -Query $query
                    Write-Console -Message "Creating session information for the selected instance of $($Global:session.resource)..."
                    $instanceProperties = @{
                        roleInstanceName = $instance.roleInstanceName
                        region = $instance.Region
                        regionShoebox = $regionShoebox.shoeboxMdmAccountName
                        nodeID = $instance.NodeID
                        bladeID = $instance.NodeID
                        vmID = $instance.VirtualMachineUniqueId
                        tenantName = $instance.tenantName
                        cluster = $instance.Tenant
                        containerID = $instance.ContainerID
                        nodeIP = $instance.IPAddress
                        torName = $instance.EndDevice
                    }
                    $Global:session | Add-Member -NotePropertyMembers $instanceProperties
                }
            }
            # No instances were found.
            Else {
                Invoke-NoInstancesFound
                Return
            }
            # Break out of the Switch statement.
            Break
        }
        <########################################
        # End: VM instance selection.
        #########################################>


        <########################################
        # Start: VMSS instance selection.
        #########################################>

        'virtualMachineScaleSets' {
            # Locate instances of the resource.
            Write-Console "Searching for instances of $($Global:session.resource)..."
            $query = "
                cluster('AzureCM').database('AzureCM').LogContainerSnapshot
                | where PreciseTimeStamp >= datetime($($Global:session.timespanStartKusto)) and PreciseTimeStamp <= datetime($($Global:session.timespanEndKusto))
                | where subscriptionId in ('$($Global:session.subscriptionID)')
                | where roleInstanceName startswith ('_$($Global:session.resource)')
                | summarize ContainerChangeDateOrEarliestKnownDate=min(PreciseTimeStamp) by CloudName, Region, DataCenterName, Tenant, AvailabilityZone, nodeId, containerId, roleInstanceName, tenantName, virtualMachineUniqueId
                | join kind=leftouter(cluster('Azuredcm'). database('AzureDCMDb').ResourceSnapshotV1) on `$left.nodeId == `$right.ResourceId
                | extend NodeIdUpper=toupper(nodeId)
                | join kind=leftouter(cluster('aznwcc').database('aznwmds').Servers) on `$left.NodeIdUpper == `$right.NodeId
                | join kind=leftouter(cluster('aznwcc').database('aznwmds').DeviceInterfaceLinks) on `$left.DeviceName == `$right.StartDevice
                | project roleInstanceName, ContainerChangeDateOrEarliestKnownDate, Region, DataCenterName, Tenant, AvailabilityZone,virtualMachineUniqueId, nodeId, containerId, tenantName, IPAddress, EndDevice
                | order by ContainerChangeDateOrEarliestKnownDate desc
            "

            $instances = Invoke-Kusto -KustoCluster 'AzureCM' -KustoDatabase 'AzureCM' -Query $query
            # Instances were found; begin instance selection and session creation.
            If ($instances) {
                $instance = $instances | Out-GridView -Title "[Nanite] Please select an instance of $($Global:session.resource):" -OutputMode Single
                If (!$instance) {
                    Write-Console "No instances of $($Global:session.resource) were selected."
                    Return
                }
                Else {
                    # Look up the region's Shoebox name to be used in certain operations such as VMDash.
                    $query = "
                        cluster('AzureCM').database('AzureCM').LogClusterSnapshot
                        | where tenantName contains '$($instance.Tenant)'
                        | where shoeboxMdmAccountName !in ('', 'NA')
                        | distinct shoeboxMdmAccountName
                    "

                    $regionShoebox = Invoke-Kusto -KustoCluster 'AzureCM' -KustoDatabase 'AzureCM' -Query $query
                    Write-Console -Message "Creating session information for the selected instance of $($Global:session.resource)..."
                    $instanceProperties = @{
                        roleInstanceName = $instance.roleInstanceName
                        region = $instance.Region
                        regionShoebox = $regionShoebox.shoeboxMdmAccountName
                        nodeID = $instance.NodeID
                        bladeID = $instance.NodeID
                        vmID = $instance.VirtualMachineUniqueId
                        tenantName = $instance.tenantName
                        cluster = $instance.Tenant
                        containerID = $instance.ContainerID
                        nodeIP = $instance.IPAddress
                        torName = $instance.EndDevice
                    }
                    $Global:session | Add-Member -NotePropertyMembers $instanceProperties
                }
            }
            # No instances we found.
            Else {
                Invoke-NoInstancesFound
                Return
            }
        }
        Default {
            # Realistically, the unsupported resource type is caught by Test-URI in Nanite.ps1, but we Default to it here to be safe.
            Write-Console -Message "The specified resource type of $($Global:session.resourceType) is not supported."
            Exit
        }
        <########################################
        # End: VMSS instance selection.
        #########################################>

    }
    
    <########################################
    # Start: Session saving.
    #########################################>

    If ($Global:session.case) {
        $Global:session | ConvertTo-Json -Depth 10 | ForEach-Object { [System.Text.RegularExpressions.Regex]::Unescape($_) } | Out-File "$($Global:configuration.Output)\Sessions\$case.json" -Force
        Write-Console -Message "Saved session information under case number #$($Global:session.case)."
    }
    Else {
        Write-Console -Message "Not saving session information for reuse as no case number was provided."
    }
    <########################################
    # End: Session saving.
    #########################################>

}