Private/Sessions.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
<#
.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.
    #########################################>

}