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 following Kusto and Jarvis time calculations are changed then you must update the Invoke-Operation functions. # Standard is the format commonly used by Kusto and certain dashboards. Exmaple: 020-01-01T00:00:00Z. timespanStartStandard = "$(Get-Date $Global:session.start -Format s)Z" timespanEndStandard = "$(Get-Date $Global:session.end -Format s)Z" # Global is the format commonly used by certain dashboards and is calculated as delta of seconds. Example: 1590969600000. timespanStartGlobal = ([int]((Get-Date($global:session.start))-[datetime]'2020-01-01').TotalSeconds * 1000 + 1577836800000).toString() timespanEndGlobal = ([int]((Get-Date($global:session.end))-[datetime]'2020-01-01').TotalSeconds * 1000 + 1577836800000).toString() } $Global:session | Add-Member -NotePropertyMembers $sessionProperties # Begin using actual resource location processes. Switch ($Global:session.resourceType) { <######################################## # Start: AFW instance selection. #########################################> 'azureFirewalls' { # Locate instances of the resource. Write-Console "Searching for instances of $($Global:session.resource)..." $query = " cluster('Hybridnetworking').database('GatewayManager').SecureGatewayTable | where CustomerSubscriptionId has '$($Global:session.subscriptionID)' and GatewayName has '$($Global:session.resource)' | order by Timestamp desc | project Timestamp, CustomerSubscriptionId, GatewayId, GatewayName, Location, ResourceGroup, SubnetId, Version, TenantSubscriptionId, SkuName, SkuTier, VnetSubscriptionId " $instances = Invoke-Kusto -KustoCluster 'HybridNetworking' -KustoDatabase 'GatewayManager' -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 { Write-Console -Message 'Executing additional sub-queries...' # Look up the region's Shoebox name to be used in certain operations such as VMDash. $query = " cluster('azcrp').database('crp_allprod').VMApiQosEvent | where TIMESTAMP > ago(30d) | where resourceGroupName == 'armrg-$($instance.gatewayID)' | where subscriptionId == '$($instance.TenantSubscriptionID)' | where fabricTenantName != '' | distinct fabricTenantName, fabricCluster | join kind=leftouter( cluster('azurecm').database('AzureCM').LogContainerSnapshot | where TIMESTAMP > ago(30d) )on `$left.fabricTenantName == `$right.tenantName | distinct fabricTenantName, fabricCluster, Tenant " $tenants = Invoke-Kusto -KustoCluster 'azcrp' -KustoDatabase 'crp_allprod' -Query $query $query = " cluster('azcrp').database('crp_allprod').VMApiQosEvent | where TIMESTAMP > ago(30d) | where resourceGroupName == 'armrg-$($instance.gatewayID)' | where subscriptionId == '$($instance.TenantSubscriptionID)' | where fabricTenantName != '' | distinct fabricTenantName, fabricCluster | join kind=leftouter( cluster('azurecm').database('AzureCM').LogContainerSnapshot | where TIMESTAMP >=datetime('$($Global:session.timespanStartStandard)') and TIMESTAMP <=datetime('$($Global:session.timespanEndStandard)') | where Tenant !contains 'TMBOX' | extend ostype=tostring(split(billingType,'|',0)[0]) )on `$left.fabricTenantName == `$right.tenantName | summarize STARTTIME=min(TIMESTAMP), ENDTIME=max(TIMESTAMP) by nodeId, containerId, roleInstanceName, tenantName, Tenant, billingType, containerType, ostype, virtualMachineUniqueId, subscriptionId, Region | project STARTTIME, ENDTIME, nodeId, containerId, roleInstanceName, tenantName, Tenant, containerType, ostype, virtualMachineUniqueId, subscriptionId, Region | 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 | join kind=leftouter(cluster('aznwcc').database('aznwmds').DeviceStatic) on DeviceName | join kind=leftouter(cluster('aznwcc').database('aznwmds').DeviceIpInterface | where AddressesV4 != '') on DeviceName | distinct STARTTIME, ENDTIME, nodeId, AddressesV4, EndDevice, EndPort, containerId, roleInstanceName, tenantName, Tenant, ostype , containerType, DataCenter ,virtualMachineUniqueId , Region, subscriptionId | order by roleInstanceName asc, STARTTIME asc " $firewallInstances = Invoke-Kusto -KustoCluster 'azcrp' -KustoDatabase 'crp_allprod' -Query $query Write-Console -Message "Creating session information for the selected instance of $($Global:session.resource)..." $instanceProperties = @{ customerSubscriptionId = $instance.CustomerSubscriptionId gatewayID = $instance.GatewayID gatewayName = $instance.GatewayName location = $instance.Location subnetID = $instance.SubnetId version = $instance.Version tenantSubscriptionID = $instance.TenantSubscriptionID skuName = $instance.skuName skuTier = $instance.skuTier vnetSubscriptionId = $instance.VnetSubscriptionId tenants = $tenants | Select-Object fabricTenantName, fabricCluster, Tenant instances = $firewallInstances | Select-Object STARTTIME, ENDTIME, nodeId, AddressesV4, EndDevice, EndPort, containerId, roleInstanceName, tenantName, Tenant, ostype, containerType, DataCenter, virtualMachineUniqueId, Region, subscriptionId } $Global:session | Add-Member -NotePropertyMembers $instanceProperties Write-Console -Message "URI: $($Global:session.uri)" Write-Console -Message "Gateway Name: $($Global:session.gatewayName)" Write-Console -Message "Gateway Version: $($Global:session.version)" Write-Console -Message "Gateway ID: $($Global:session.gatewayID)" Write-Console -Message "SKU Name: $($Global:session.skuName)" Write-Console -Message "SKU Tier: $($Global:session.skuTier)" Write-Console -Message "Subnet Resource ID: $($Global:session.subnetID)" Write-Console -Message "Location: $($Global:session.Location)" $i = 0 ForEach ($tenant in $Global:session.tenants) { Write-Console -Message "Tenant #$($i + 1)" Write-Console -Message "- Deployment ID: $($tenant.fabricTenantName)" Write-Console -Message "- Fabric GeoID Name: $($tenant.Tenant)" $firewallInstances = $Global:session.instances | Where-Object { $_.Tenant -eq $tenant.Tenant } $i2 = 0 ForEach ($firewallInstance in $firewallInstances) { Write-Console -Message "- Instance #$($i2 + 1)" Write-Console -Message "-- Container ID: $($firewallInstance.containerID)" Write-Console -Message "-- Node ID: $($firewallInstance.nodeID)" Write-Console -Message "-- Role Instance Name: $($firewallInstance.roleInstanceName)" Write-Console -Message "-- Node IP: $($firewallInstance.AddressesV4)" $i2++ } $i++ } } } # No instances were found. Else { Invoke-NoInstancesFound Return } # Break out of the Switch statement. Break } <######################################## # End: AFW instance selection. #########################################> <######################################## # Start: AKS instance selection. #########################################> 'managedClusters' { Write-Console "Searching for instance information for $($Global:session.resource)..." $query = " cluster('aks').database('AKSprod').BlackboxMonitoringActivity | where PreciseTimeStamp > ago(10d) | where subscriptionID == '$($Global:session.subscriptionID)' and resourceGroupName has '$($Global:session.resourceGroup)' and clusterName has '$($Global:session.resource)' | summarize by fqdn, resourceGroupName, resourceName, underlayName " $instances = Invoke-Kusto -KustoCluster 'aks' -KustoDatabase 'AKSprod' -Query $query $instanceProperties = @{ blackBox = $instances.fqdn cluster = $Global:session.resource underlay = $instances.underlayName } $Global:session | Add-Member -NotePropertyMembers $instanceProperties Write-Console -Message "Creating session information for $($Global:session.resource)..." Write-Console -Message "URI: $($Global:session.uri)" Write-Console -Message "Cluster: $($Global:session.resource)" Write-Console -Message "Black Box FQDN: $($Global:session.blackBox)" Write-Console -Message "Underlay: $($Global:session.underlay)" # Break out of the Switch statement. Break } <######################################## # End: AKS instance selection. #########################################> <######################################## # Start: ANF instance selection. #########################################> 'netAppAccounts' { Write-Console "Searching for instance information for $($Global:session.resource)..." $instanceProperties = @{ account = $Global:session.resource capacityPool = $tokens[10] volume = $tokens[12] snapshot = $tokens[14] } $Global:session | Add-Member -NotePropertyMembers $instanceProperties Write-Console -Message "Creating session information for $($Global:session.resource)..." Write-Console -Message "URI: $($Global:session.uri)" Write-Console -Message "NetApp Account: $($Global:session.resource)" Write-Console -Message "Capacity Pool: $($Global:session.capacityPool)" # Break out of the Switch statement. Break } <######################################## # End: ANF instance selection. #########################################> <######################################## # Start: ExR instance selection. #########################################> 'expressRouteCircuits' { # Locate instances of the resource. Write-Console "Searching for instances of $($Global:session.resource)..." $query = " cluster('aznw').database('aznwcosmos').['ExpressRoute.CircuitTable'] | where NrpResourceUri endswith '$($Global:session.uri)' | summarize arg_max(Timestamp, *) by AzureServiceKey, CircuitName, CircuitType, ServiceProviderName, Location, Bandwidth, BillingType, CreationTime, NumberOfConfiguredLinks, PrimaryDeviceName, SecondaryDeviceName, NrpResourceUri | project Timestamp, CreationTime, AzureServiceKey, CircuitName, CircuitType, ServiceProviderName, Location, Bandwidth, BillingType, NumberOfConfiguredLinks, PrimaryDeviceName, SecondaryDeviceName, NrpResourceUri " $instances = Invoke-Kusto -KustoCluster 'aznw' -KustoDatabase 'aznwcosmos' -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 { Write-Console -Message 'Executing additional sub-queries...' # Look up the region's Shoebox name to be used in certain operations such as VMDash. $query = " cluster('aznw').database('aznwcosmos').['ExpressRoute.SubinterfaceTable'] | where ServiceKey == '$($instance.AzureServiceKey)' | distinct Cloud, Description, ServiceKey, InterfaceName, IpAddress, NetworkMask, DeviceName, Type, VrfId, SubscriptionId | order by Type, DeviceName asc " $subinterfaces = Invoke-Kusto -KustoCluster 'aznw' -KustoDatabase 'aznwcosmos' -Query $query If ($subinterfaces) { Write-Console -Message "Creating session information for the selected instance of $($Global:session.resource)..." $instanceProperties = @{ creationTime = $instance.CreationTime azureServiceKey = $instance.AzureServiceKey circuitName = $instance.CircuitName circuitType = $instance.CircuitType serviceProviderName = $instance.ServiceProviderName location = $instance.Location bandwidth = $instance.Bandwidth billingType = $instance.BillingType numberOfConfiguredLinks = $instance.NumberOfConfiguredLinks primaryDeviceName = $instance.PrimaryDeviceName secondaryDeviceName = $instance.SecondaryDeviceName nrpResourceURI = $instance.NrpResourceUri # Subinterfaces has to have explicit column selection or else the entire System.Data.DataRowView will be saved, resulting in issues with saving the session due to JSON conversions. subinterfaces = $subinterfaces | Select-Object Cloud, Description, ServiceKey, InterfaceName, IpAddress, NetworkMask, DeviceName, Type, VrfId, SubscriptionId } $Global:session | Add-Member -NotePropertyMembers $instanceProperties Write-Console -Message "URI: $($Global:session.uri)" Write-Console -Message "Service Key: $($Global:session.azureServiceKey)" Write-Console -Message "Circuit Name: $($Global:session.circuitName)" Write-Console -Message "Service Provider Name: $($Global:session.serviceProviderName)" Write-Console -Message "Location: $($Global:session.location)" Write-Console -Message "Bandwidth: $($Global:session.bandwidth)" Write-Console -Message "Billing Type: $($Global:session.billingType)" $i = 0 ForEach ($subinterface in $Global:session.subinterfaces) { Write-Console -Message "Subinterface #$($i + 1)" Write-Console -Message "- Interface Name: $($subinterface.InterfaceName)" Write-Console -Message "- Device: $($subinterface.DeviceName) ($($subinterface.Type))" Write-Console -Message "- Description: $($subinterface.Description)" Write-Console -Message "- Addressing: $($subinterface.IpAddress)/$($subinterface.NetworkMask)" $i++ } } Else { Write-Console 'Could not locate the required subinterface information.' Return } } } # No instances were found. Else { Invoke-NoInstancesFound Return } # Break out of the Switch statement. Break } <######################################## # End: ExR instance selection. #########################################> <######################################## # Start: Storage Account instance selection. #########################################> 'storageAccounts' { Write-Console "Searching for instance information for $($Global:session.resource)..." $instanceProperties = @{ blob = $null file = $null queue = $null stamp = $null stampDeployment = $null stampFQDN = $null table = $null } # Try to resolve each storage service endpoint. # If there is resolution then the storage service is used. # If there is no resolution then there is no service. $recordsFound = @() ForEach ($storageType in @('blob', 'file', 'queue', 'table')) { If ($record = Resolve-DnsName -Name "$($session.resource).$storageType.core.windows.net" -ErrorAction SilentlyContinue) { $recordsFound += $record $instanceProperties.$storageType = "$($session.resource).$storageType.core.windows.net" } } # There was at least one storage service record returned. If ($recordsFound.Count -gt 0) { $instanceProperties.stampFQDN = ($recordsFound[0].NameHost) $instanceProperties.stamp = ($instanceProperties.stampFQDN).Split('.')[1] $instanceProperties.stampDeployment = "ms-$($instanceProperties.stamp)" $Global:session | Add-Member -NotePropertyMembers $instanceProperties Write-Console -Message "Creating session information for $($Global:session.resource)..." Write-Console -Message "Storage Account: $($Global:session.resource)" Write-Console -Message "Deployment Stamp: $($Global:session.stampDeployment)" } # No storage services resolved meaning that no storage services exist for the account. Else { Write-Console -Message "No storage services were found for $($session.resource)." Return } # Break out of the Switch statement. Break } <######################################## # End: Storage Account instance selection. #########################################> <######################################## # 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.timespanStartStandard)) and PreciseTimeStamp <= datetime($($Global:session.timespanEndStandard)) | where subscriptionId in ('$($Global:session.subscriptionID)') | where roleInstanceName in ('_$($Global:session.resource)', '$($Global:session.resource)') | distinct creationTime, 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, creationTime, Region, DataCenterName, Tenant, AvailabilityZone, virtualMachineUniqueId, nodeId, containerId, tenantName, IPAddress, EndDevice | order by creationTime 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 { Write-Console -Message 'Executing additional sub-queries...' # 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 = @{ creationTime = $instance.creationTime 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 Write-Console -Message "URI: $($Global:session.uri)" Write-Console -Message "Role Instance Name: $($Global:session.roleInstanceName)" Write-Console -Message "VM ID: $($Global:session.vmID)" Write-Console -Message "Creation Time: $($Global:session.creationTime)" Write-Console -Message "Region: $($Global:session.region)" Write-Console -Message "Cluster: $($Global:session.cluster)" Write-Console -Message "Node ID: $($Global:session.nodeID)" } } # 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.timespanStartStandard)) and PreciseTimeStamp <= datetime($($Global:session.timespanEndStandard)) | where subscriptionId in ('$($Global:session.subscriptionID)') | where roleInstanceName startswith ('_$($Global:session.resource)') | distinct creationTime, 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, creationTime, Region, DataCenterName, Tenant, AvailabilityZone,virtualMachineUniqueId, nodeId, containerId, tenantName, IPAddress, EndDevice | order by creationTime 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 { Write-Console -Message 'Executing additional sub-queries...' # 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 = @{ creationTime = $instance.creationTime 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 Write-Console -Message "URI: $($Global:session.uri)" Write-Console -Message "Role Instance Name: $($Global:session.roleInstanceName)" Write-Console -Message "VM ID: $($Global:session.vmID)" Write-Console -Message "Creation Time: $($Global:session.creationTime)" Write-Console -Message "Region: $($Global:session.region)" Write-Console -Message "Cluster: $($Global:session.cluster)" Write-Console -Message "Node ID: $($Global:session.nodeID)" } } # 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 5 | ForEach-Object { [System.Text.RegularExpressions.Regex]::Unescape($_) } | Out-File "$($Global:configuration.Output)\Sessions\$($Global:session.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. #########################################> } |